Initial commit.

This commit is contained in:
Jackymancs4
2015-09-24 16:21:05 +02:00
parent 11ccbbf901
commit 949f9f232c
8 changed files with 1202 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
* {
margin: 0;
padding: 0;
border: 0;
box-sizing: border-box;
font-family: sans-serif;
font-size: 11px;
}
body {
width: 100%;
}
h1 {
text-align: center;
margin: 10px;
font-size: 18px;
}
h2 {
text-align: center;
margin: 5px 0;
}
h3 {
text-align: center;
font-style: italic;
font-size: 10px;
margin-bottom: 10px;
font-weight: normal;
}
h4 {
font-size: 14px;
margin-top: 10px;
margin-bottom: 4px;
}
h4:first-child {
margin-top: 0;
}
select {
border: 1px solid #ddd;
}
#main_holder {
width: 800px;
height: auto;
margin: 0 auto;
padding-bottom: 20px;
}
#main_screen {
border: 1px solid #888;
}
#info {
width: 100%;
}
#info>div {
vertical-align: top;
margin-right: 15px;
display: inline-block;
width: 180px;
}
.name_list_table {
border-collapse: collapse;
min-width: 180px;
}
.name_list_table td, .name_list_table th {
border: 1px solid #888;
padding: 1px 3px;
}
.name_list_table .name {
text-transform: capitalize;
}
.name_list_table .elite_name {
color: #b40;
}
.name_list_table .score {
text-align: right;
}
.name_list_table .generation {
text-align: right;
}
#hidden_ss {
height: 0;
width: 0;
display: none;
}
#generation_timer {
width: 100%;
height: 10px;
}
#generation_timer_bar {
background-color: #bbf;
height: 100%;
width: 0;
font-size: 0;
max-width: 100%;
}
+127
View File
@@ -0,0 +1,127 @@
<!doctype html>
<html>
<head>
<title>HTML5 Genetic Algorithm Biped Walkers</title>
<link rel="stylesheet" href="css/walkers.css" type="text/css" />
<script src="js/jsbox2d.js"></script>
<script src="js/walker.js"></script>
<script src="js/game.js"></script>
<script src="js/floor.js"></script>
<script src="js/draw.js"></script>
<script src="js/interface.js"></script>
<script>
function init() {
gameInit();
}
window.addEventListener("load", init, false);
</script>
</head>
<body>
<div id="main_holder">
<h1>Genetic Algorithm Walkers</h1>
<h3 id="page_quote">"It's like watching other people QWOP"</h3>
<canvas id="main_screen" width="800" height="350"></canvas>
<div id="generation_timer"><div id="generation_timer_bar">&nbsp;</div></div>
<div id="info">
<div>
<h2>Current Generation (<span id="gen_number">0</span>)</h2>
<table class="name_list_table">
<thead>
<tr>
<th>Name</th>
<th>Score</th>
</tr>
</thead>
<tbody id="name_list">
</tbody>
</table>
</div>
<div>
<h2>Record History</h2>
<table class="name_list_table">
<thead>
<tr>
<th>Gen</th>
<th>Name</th>
<th>Score</th>
</tr>
</thead>
<tbody id="champ_list">
</tbody>
</table>
</div>
<div id="controls">
<h2>Controls</h2>
Gene mutation probability
<select id="mutation_chance">
<option value="0">0%</option>
<option value="0.01">1%</option>
<option value="0.05">5%</option>
<option value="0.1">10%</option>
<option value="0.25">25%</option>
<option value="0.5">50%</option>
<option value="0.75">75%</option>
<option value="1">100%</option>
</select><br />
Gene mutation amount
<select id="mutation_amount">
<option value="0">0%</option>
<option value="0.01">1%</option>
<option value="0.05">5%</option>
<option value="0.1">10%</option>
<option value="0.25">25%</option>
<option value="0.5">50%</option>
<option value="0.75">75%</option>
<option value="1">100%</option>
</select><br />
Champions to copy
<select id="elite_clones"></select><br />
Motor noise
<select id="motor_noise">
<option value="0">0%</option>
<option value="0.01">1%</option>
<option value="0.02">2%</option>
<option value="0.03">3%</option>
<option value="0.04">4%</option>
<option value="0.05">5%</option>
<option value="0.1">10%</option>
</select><br />
Round length
<select id="round_length">
<option value="300">Very short</option>
<option value="600">Short</option>
<option value="1200">Regular</option>
<option value="1800">Long</option>
<option value="2400">Looooong</option>
</select><br />
Animation quality
<select id="draw_fps">
<option value="0">none</option>
<option value="10">10 fps</option>
<option value="30">30 fps</option>
<option value="60">60 fps</option>
</select><br />
Simulation speed
<select id="simulation_fps">
<option value="0">Pause</option>
<option value="30">30</option>
<option value="60">60</option>
<option value="120">120</option>
<option value="1000">1000</option>
</select><br />
<br />
Lower the animation quality and simulation speed to save some CPU.<br /><br />
Zero frames per second and high simulation speed will give you the fastest results, but processor usage will go up as well.
</div>
</div>
</body>
</html>
+144
View File
@@ -0,0 +1,144 @@
drawInit = function() {
globals.main_screen = document.getElementById("main_screen");
globals.ctx = main_screen.getContext("2d");
resetCamera();
}
resetCamera = function() {
globals.zoom = config.max_zoom_factor;
globals.translate_x = 0;
globals.translate_y = 280;
}
setFps = function(fps) {
config.draw_fps = fps;
if(globals.draw_interval)
clearInterval(globals.draw_interval);
if(fps > 0 && config.simulation_fps > 0) {
globals.draw_interval = setInterval(drawFrame, Math.round(1000/config.draw_fps));
}
}
drawFrame = function() {
var minmax = getMinMaxDistance();
globals.target_zoom = Math.min(config.max_zoom_factor, getZoom(minmax.min_x, minmax.max_x + 4, minmax.min_y + 2, minmax.max_y + 2.5));
globals.zoom += 0.1*(globals.target_zoom - globals.zoom);
globals.translate_x += 0.1*(1.5-minmax.min_x - globals.translate_x);
globals.translate_y += 0.3*(minmax.min_y*globals.zoom + 280 - globals.translate_y);
//globals.translate_y = minmax.max_y*globals.zoom + 150;
globals.ctx.clearRect(0, 0, globals.main_screen.width, globals.main_screen.height);
globals.ctx.save();
globals.ctx.translate(globals.translate_x*globals.zoom, globals.translate_y);
globals.ctx.scale(globals.zoom, -globals.zoom);
drawFloor();
for(var k = config.population_size - 1; k >= 0 ; k--) {
if(globals.walkers[k].health > 0) {
drawWalker(globals.walkers[k]);
}
}
globals.ctx.restore();
}
drawFloor = function() {
globals.ctx.strokeStyle = "#444";
globals.ctx.lineWidth = 1/globals.zoom;
globals.ctx.beginPath();
var floor_fixture = globals.floor.GetFixtureList();
globals.ctx.moveTo(floor_fixture.m_shape.m_vertices[0].x, floor_fixture.m_shape.m_vertices[0].y);
for(var k = 1; k < floor_fixture.m_shape.m_vertices.length; k++) {
globals.ctx.lineTo(floor_fixture.m_shape.m_vertices[k].x, floor_fixture.m_shape.m_vertices[k].y);
}
globals.ctx.stroke();
}
drawWalker = function(walker) {
if(walker.is_elite) {
//globals.ctx.strokeStyle = "hsl(22,"+100*walker.health/config.walker_health+"%,35%)";
// globals.ctx.fillStyle = "hsl(20,"+45*walker.health/config.walker_health+"%,85%)";
globals.ctx.strokeStyle = "hsl(22,100%,"+(90-55*walker.health/config.walker_health)+"%)";
globals.ctx.fillStyle = "hsl(20,45%,"+(100-15*walker.health/config.walker_health)+"%)";
} else {
//globals.ctx.strokeStyle = "hsl(240,"+100*walker.health/config.walker_health+"%,41%)";
// globals.ctx.fillStyle = "hsl(240,"+17*walker.health/config.walker_health+"%,85%)";
globals.ctx.strokeStyle = "hsl(240,100%,"+(90-49*walker.health/config.walker_health)+"%)";
globals.ctx.fillStyle = "hsl(240,45%,"+(100-15*walker.health/config.walker_health)+"%)";
}
globals.ctx.lineWidth = 1/globals.zoom;
// left legs and arms first
drawRect(walker.left_arm.lower_arm);
drawRect(walker.left_arm.upper_arm);
drawRect(walker.left_leg.foot);
drawRect(walker.left_leg.lower_leg);
drawRect(walker.left_leg.upper_leg);
// head
drawRect(walker.head.neck);
drawRect(walker.head.head);
// torso
drawRect(walker.torso.lower_torso);
drawRect(walker.torso.upper_torso);
// right legs and arms
drawRect(walker.right_leg.upper_leg);
drawRect(walker.right_leg.lower_leg);
drawRect(walker.right_leg.foot);
drawRect(walker.right_arm.upper_arm);
drawRect(walker.right_arm.lower_arm);
}
drawRect = function(body) {
// set strokestyle and fillstyle before call
globals.ctx.beginPath();
var fixture = body.GetFixtureList();
var shape = fixture.GetShape();
var p0 = body.GetWorldPoint(shape.m_vertices[0]);
globals.ctx.moveTo(p0.x, p0.y);
for(var k = 1; k < 4; k++) {
var p = body.GetWorldPoint(shape.m_vertices[k]);
globals.ctx.lineTo(p.x, p.y);
}
globals.ctx.lineTo(p0.x, p0.y);
globals.ctx.fill();
globals.ctx.stroke();
}
drawTest = function() {
globals.ctx.strokeStyle = "#000";
globals.ctx.fillStyle = "#666";
globals.ctx.lineWidth = 1;
globals.ctx.beginPath();
globals.ctx.moveTo(0, 0);
globals.ctx.lineTo(0, 2);
globals.ctx.lineTo(2, 2);
globals.ctx.fill();
globals.ctx.stroke();
}
getMinMaxDistance = function() {
var min_x = 9999;
var max_x = -1;
var min_y = 9999;
var max_y = -1;
for(var k = 0; k < globals.walkers.length; k++) {
if(globals.walkers[k].health > 0) {
var dist = globals.walkers[k].torso.upper_torso.GetPosition();
min_x = Math.min(min_x, dist.x);
max_x = Math.max(max_x, dist.x);
min_y = Math.min(min_y, globals.walkers[k].low_foot_height, globals.walkers[k].head_height);
max_y = Math.max(max_y, dist.y);
}
}
return {min_x:min_x, max_x:max_x, min_y:min_y, max_y:max_y};
}
getZoom = function(min_x, max_x, min_y, max_y) {
var delta_x = Math.abs(max_x - min_x);
var delta_y = Math.abs(max_y - min_y);
var zoom = Math.min(globals.main_screen.width/delta_x,globals.main_screen.height/delta_y);
return zoom;
}
+22
View File
@@ -0,0 +1,22 @@
function createFloor() {
var body_def = new b2.BodyDef();
var body = globals.world.CreateBody(body_def);
var fix_def = new b2.FixtureDef();
fix_def.friction = 0.8;
fix_def.shape = new b2.ChainShape();
// start with flat floor
var edges = [
new b2.Vec2(-3.5, -0.16),
new b2.Vec2(2.5, -0.16)
];
for(var k = 2; k < config.max_floor_tiles; k++) {
var ratio = k/config.max_floor_tiles;
edges.push(new b2.Vec2(edges[edges.length-1].x + (1 + ratio * Math.random() - ratio/2), edges[edges.length-1].y + (ratio * Math.random() - ratio/2 )));
// edges.push(new b2.Vec2(edges[edges.length-1].x + 1,-0.16));
}
fix_def.shape.CreateChain(edges, edges.length);
body.CreateFixture(fix_def);
return body;
}
+242
View File
@@ -0,0 +1,242 @@
config = {
time_step: 60,
simulation_fps: 60,
draw_fps: 60,
velocity_iterations: 8,
position_iterations: 3,
max_zoom_factor: 130,
min_motor_speed: -2,
max_motor_speed: 2,
population_size: 20,
mutation_chance: 0.1,
mutation_amount: 0.5,
walker_health: 300,
fitness_criterium: 'score',
check_health: true,
elite_clones: 2,
max_floor_tiles: 50,
round_length: 1200,
min_body_delta: 1.4,
min_leg_delta: 0.4,
instadeath_delta: 0.4
};
globals = {};
gameInit = function() {
interfaceSetup();
globals.world = new b2.World(new b2.Vec2(0, -10));
globals.walkers = createPopulation();
globals.floor = createFloor();
drawInit();
globals.step_counter = 0;
globals.simulation_interval = setInterval(simulationStep, Math.round(1000/config.simulation_fps));
globals.draw_interval = setInterval(drawFrame, Math.round(1000/config.draw_fps));
}
simulationStep = function() {
globals.world.Step(1/config.time_step, config.velocity_iterations, config.position_iterations);
globals.world.ClearForces();
populationSimulationStep();
if(typeof globals.step_counter == 'undefined') {
globals.step_counter = 0;
} else {
globals.step_counter++;
}
document.getElementById("generation_timer_bar").style.width = (100*globals.step_counter/config.round_length)+"%";
if(globals.step_counter > config.round_length) {
nextGeneration();
}
}
setSimulationFps = function(fps) {
config.simulation_fps = fps;
clearInterval(globals.simulation_interval);
if(fps > 0) {
globals.simulation_interval = setInterval(simulationStep, Math.round(1000/config.simulation_fps));
if(globals.paused) {
globals.paused = false;
if(config.draw_fps > 0) {
globals.draw_interval = setInterval(drawFrame, Math.round(1000/config.draw_fps));
}
}
} else {
// pause the drawing as well
clearInterval(globals.draw_interval);
globals.paused = true;
}
}
createPopulation = function(genomes) {
setQuote();
if(typeof globals.generation_count == 'undefined') {
globals.generation_count = 0;
} else {
globals.generation_count++;
}
updateGeneration(globals.generation_count);
var walkers = [];
for(var k = 0; k < config.population_size; k++) {
if(genomes && genomes[k]) {
walkers.push(new Walker(globals.world, genomes[k]));
} else {
walkers.push(new Walker(globals.world));
}
if(globals.generation_count > 0 && k < config.elite_clones) {
walkers[walkers.length - 1].is_elite = true;
} else {
walkers[walkers.length - 1].is_elite = false;
}
}
return walkers;
}
populationSimulationStep = function() {
var dead_dudes = 0;
for(var k = 0; k < config.population_size; k++) {
if(globals.walkers[k].health > 0) {
globals.walkers[k].simulationStep();
} else {
if(!globals.walkers[k].is_dead) {
for(var l = 0; l < globals.walkers[k].bodies.length; l++) {
if(globals.walkers[k].bodies[l]) {
globals.world.DestroyBody(globals.walkers[k].bodies[l]);
globals.walkers[k].bodies[l] = null;
}
}
globals.walkers[k].is_dead = true;
}
dead_dudes++;
}
}
printNames(globals.walkers);
if(dead_dudes >= config.population_size) {
nextGeneration();
}
}
nextGeneration = function() {
if(globals.simulation_interval)
clearInterval(globals.simulation_interval);
if(globals.draw_interval)
clearInterval(globals.draw_interval);
getInterfaceValues();
var genomes = createNewGenerationGenomes();
killGeneration();
globals.walkers = createPopulation(genomes);
resetCamera();
globals.step_counter = 0;
globals.simulation_interval = setInterval(simulationStep, Math.round(1000/config.simulation_fps));
if(config.draw_fps > 0) {
globals.draw_interval = setInterval(drawFrame, Math.round(1000/config.draw_fps));
}
}
killGeneration = function() {
for(var k = 0; k < globals.walkers.length; k++) {
for(var l = 0; l < globals.walkers[k].bodies.length; l++) {
if(globals.walkers[k].bodies[l])
globals.world.DestroyBody(globals.walkers[k].bodies[l]);
}
}
}
createNewGenerationGenomes = function() {
globals.walkers.sort(function(a,b) {
return b[config.fitness_criterium] - a[config.fitness_criterium];
});
if(typeof globals.last_record == 'undefined') {
globals.last_record = 0;
}
if(globals.walkers[0][config.fitness_criterium] > globals.last_record) {
printChampion(globals.walkers[0]);
globals.last_record = globals.walkers[0][config.fitness_criterium];
}
var genomes = [];
var parents = null;
// clones
for(var k = 0; k < config.elite_clones; k++) {
genomes.push(globals.walkers[k].genome);
}
for(var k = config.elite_clones; k < config.population_size; k++) {
if(parents = pickParents()) {
genomes.push(copulate(globals.walkers[parents[0]], globals.walkers[parents[1]]));
}
}
genomes = mutateClones(genomes);
return genomes;
}
pickParents = function() {
var parents = [];
for(var k = 0; k < config.population_size; k++) {
if(Math.random() < (1/(k+2))) {
parents.push(k);
if(parents.length >= 2) {
break;
}
}
}
if(typeof parents[0] == 'undefined' || typeof parents[1] == 'undefined') {
return false;
}
return parents;
}
copulate = function(walker_1, walker_2) {
var new_genome = [];
for(var k = 0; k < walker_1.genome.length; k++) {
if(Math.random() < 0.5) {
var parent = walker_1;
} else {
var parent = walker_2
}
var new_gene = JSON.parse(JSON.stringify(parent.genome[k]));
for(var g in walker_1.genome[k]) {
if(walker_1.genome[k].hasOwnProperty(g)) {
if(Math.random() < config.mutation_chance) {
if(g.indexOf('target') >= 0) {
new_gene[g] = Math.floor(Math.random() * walker_1.bodies.length);
} else {
new_gene[g] = new_gene[g] * (1 + config.mutation_amount*(Math.random()*2 - 1));
}
}
}
}
new_genome[k] = new_gene;
}
return new_genome;
}
mutateClones = function(genomes) {
if(parseFloat(config.mutation_chance) == 0) {
return genomes;
}
for(var k = 0; k < genomes.length; k++) {
var current = JSON.stringify(genomes[k]);
for(var l = k + 1; l < genomes.length; l++) {
if(current == JSON.stringify(genomes[l])) {
var to_mutate = Math.floor(Math.random() * genomes[l].length);
for(var g in genomes[l][to_mutate]) {
if(genomes[l][to_mutate].hasOwnProperty(g)) {
if(g.indexOf('target') < 0) {
genomes[l][to_mutate][g] = genomes[l][to_mutate][g] * (1 + config.mutation_amount*(Math.random()*2 - 1));
}
}
}
}
}
}
return genomes;
}
getInterfaceValues = function() {
config.elite_clones = document.getElementById("elite_clones").value;
config.mutation_chance = document.getElementById("mutation_chance").value
config.mutation_amount = document.getElementById("mutation_amount").value
}
+131
View File
@@ -0,0 +1,131 @@
quotes = [
"The Ministry of Silly Walks on steroids",
"It's like watching other people QWOP",
"Keep tumbling",
"As seen on a negative comment on Reddit",
"The Walking Sad",
"The Walking Fad",
"Army of QWOP",
"Nice me-and-my-friends-leaving-the-bar-at-2am simulator!"
];
printNames = function(walkers) {
var name_list = document.getElementById("name_list");
name_list.innerHTML = "";
for(var k = 0; k < walkers.length; k++) {
var tr = document.createElement("TR");
if(walkers[k].is_elite) {
tr.className = "elite_name";
}
var td = document.createElement("TD");
td.className = "name";
td.appendChild(document.createTextNode(walkers[k].name));
tr.appendChild(td);
td = document.createElement("TD");
td.className = "score";
td.appendChild(document.createTextNode(walkers[k][config.fitness_criterium].toFixed(2)));
tr.appendChild(td);
name_list.appendChild(tr);
}
}
printChampion = function(walker) {
var champ_list = document.getElementById("champ_list");
var tr = document.createElement("TR");
var cur_rows = champ_list.getElementsByTagName("TR");
if(cur_rows.length >= config.population_size) {
champ_list.removeChild(cur_rows[0]);
}
var td = document.createElement("TD");
td.className = "generation";
td.appendChild(document.createTextNode(globals.generation_count));
tr.appendChild(td);
td = document.createElement("TD");
td.className = "name";
td.appendChild(document.createTextNode(walker.name));
tr.appendChild(td);
td = document.createElement("TD");
td.className = "score";
td.appendChild(document.createTextNode(walker[config.fitness_criterium].toFixed(2)));
tr.appendChild(td);
champ_list.appendChild(tr);
}
updateGeneration = function(number) {
document.getElementById("gen_number").innerHTML = number;
}
setQuote = function() {
document.getElementById("page_quote").innerHTML = '"'+quotes[Math.floor(Math.random() * quotes.length)]+'"';
}
interfaceSetup = function() {
var mut_chance_sel = document.getElementById("mutation_chance");
for(var k = 0; k < mut_chance_sel.options.length; k++) {
if(mut_chance_sel.options[k].value == config.mutation_chance) {
mut_chance_sel.options[k].selected = true;
break;
}
}
var mut_amount_sel = document.getElementById("mutation_amount");
for(var k = 0; k < mut_amount_sel.options.length; k++) {
if(mut_amount_sel.options[k].value == config.mutation_amount) {
mut_amount_sel.options[k].selected = true;
break;
}
}
var elite_clones_sel = document.getElementById("elite_clones");
for(var k = 0; k <= config.population_size; k++) {
var option = document.createElement("OPTION");
option.value = k;
option.label = k;
if(k == config.elite_clones) {
option.selected = true;
}
elite_clones_sel.appendChild(option);
}
var round_length_sel = document.getElementById("round_length");
for(var k = 0; k <= round_length_sel.options.length; k++) {
if(round_length_sel.options[k].value == config.round_length) {
round_length_sel.options[k].selected = true;
break;
}
}
round_length_sel.onchange = function() {
config.round_length = round_length_sel.value;
}
var fps_sel = document.getElementById("draw_fps");
for(var k = 0; k < fps_sel.options.length; k++) {
if(fps_sel.options[k].value == config.draw_fps) {
fps_sel.options[k].selected = true;
break;
}
}
fps_sel.onchange = function() {
setFps(fps_sel.value);
}
var simulation_fps_sel = document.getElementById("simulation_fps");
for(var k = 0; k < simulation_fps_sel.options.length; k++) {
if(simulation_fps_sel.options[k].value == config.simulation_fps) {
simulation_fps_sel.options[k].selected = true;
break;
}
}
simulation_fps_sel.onchange = function() {
setSimulationFps(simulation_fps_sel.value);
}
}
+1
View File
File diff suppressed because one or more lines are too long
+423
View File
@@ -0,0 +1,423 @@
// walker has fixed shapes and structures
// shape definitions are in the constructor
var Walker = function() {
this.__constructor.apply(this, arguments);
}
Walker.prototype.__constructor = function(world, genome) {
this.world = globals.world;
this.density = 106.2; // common for all fixtures, no reason to be too specific
this.max_distance = -5;
this.health = config.walker_health;
this.score = 0;
this.low_foot_height = 0;
this.head_height = 0;
this.steps = 0;
this.bd = new b2.BodyDef();
this.bd.type = b2.Body.b2_dynamicBody;
this.bd.linearDamping = 0;
this.bd.angularDamping = 0.01;
this.bd.allowSleep = true;
this.bd.awake = true;
this.fd = new b2.FixtureDef();
this.fd.density = this.density;
this.fd.restitution = 0.1;
this.fd.shape = new b2.PolygonShape();
this.fd.filter.groupIndex = -1;
this.torso_def = {
upper_width: 0.25,
upper_height: 0.45,
lower_width: 0.25,
lower_height: 0.2
};
this.leg_def = {
femur_width: 0.18,
femur_length: 0.45,
tibia_width: 0.13,
tibia_length: 0.38,
foot_height: 0.08,
foot_length: 0.28
};
this.arm_def = {
arm_width: 0.12,
arm_length: 0.37,
forearm_width: 0.1,
forearm_length: 0.42
}
this.head_def = {
head_width: 0.22,
head_height: 0.22,
neck_width: 0.1,
neck_height: 0.08
}
this.joints = [];
this.torso = this.createTorso();
this.left_leg = this.createLeg();
this.right_leg = this.createLeg();
this.left_arm = this.createArm();
this.right_arm = this.createArm();
this.head = this.createHead();
this.connectParts();
this.bodies = this.getBodies();
if(genome) {
this.genome = JSON.parse(JSON.stringify(genome));
} else {
this.genome = this.createGenome(this.joints, this.bodies);
}
this.name = this.makeName(this.genome);
}
Walker.prototype.createTorso = function() {
// upper torso
this.bd.position.Set(0.5 - this.leg_def.foot_length/2 + this.leg_def.tibia_width/2, this.leg_def.foot_height/2 + this.leg_def.foot_height/2 + this.leg_def.tibia_length + this.leg_def.femur_length + this.torso_def.lower_height + this.torso_def.upper_height/2);
var upper_torso = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.torso_def.upper_width/2, this.torso_def.upper_height/2);
upper_torso.CreateFixture(this.fd);
// lower torso
this.bd.position.Set(0.5 - this.leg_def.foot_length/2 + this.leg_def.tibia_width/2, this.leg_def.foot_height/2 + this.leg_def.foot_height/2 + this.leg_def.tibia_length + this.leg_def.femur_length + this.torso_def.lower_height/2);
var lower_torso = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.torso_def.lower_width/2, this.torso_def.lower_height/2);
lower_torso.CreateFixture(this.fd);
// torso joint
var jd = new b2.RevoluteJointDef();
var position = upper_torso.GetPosition().Clone();
position.y -= this.torso_def.upper_height/2;
position.x -= this.torso_def.lower_width/3;
jd.Initialize(upper_torso, lower_torso, position);
jd.lowerAngle = -Math.PI/18;
jd.upperAngle = Math.PI/10;
jd.enableLimit = true;
jd.maxMotorTorque = 250;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
return {upper_torso: upper_torso, lower_torso: lower_torso};
}
Walker.prototype.createLeg = function() {
// upper leg
this.bd.position.Set(0.5 - this.leg_def.foot_length/2 + this.leg_def.tibia_width/2, this.leg_def.foot_height/2 + this.leg_def.foot_height/2 + this.leg_def.tibia_length + this.leg_def.femur_length/2);
var upper_leg = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.leg_def.femur_width/2, this.leg_def.femur_length/2);
upper_leg.CreateFixture(this.fd);
// lower leg
this.bd.position.Set(0.5 - this.leg_def.foot_length/2 + this.leg_def.tibia_width/2, this.leg_def.foot_height/2 + this.leg_def.foot_height/2 + this.leg_def.tibia_length/2);
var lower_leg = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.leg_def.tibia_width/2, this.leg_def.tibia_length/2);
lower_leg.CreateFixture(this.fd);
// foot
this.bd.position.Set(0.5, this.leg_def.foot_height/2);
var foot = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.leg_def.foot_length/2, this.leg_def.foot_height/2);
foot.CreateFixture(this.fd);
// leg joints
var jd = new b2.RevoluteJointDef();
var position = upper_leg.GetPosition().Clone();
position.y -= this.leg_def.femur_length/2;
position.x += this.leg_def.femur_width/4;
jd.Initialize(upper_leg, lower_leg, position);
jd.lowerAngle = -1.4;
jd.upperAngle = 0;
jd.enableLimit = true;
jd.maxMotorTorque = 160;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
// foot joint
var position = lower_leg.GetPosition().Clone();
position.y -= this.leg_def.tibia_length/2;
jd.Initialize(lower_leg, foot, position);
jd.lowerAngle = -Math.PI/5;
jd.upperAngle = Math.PI/6;
jd.enableLimit = true;
jd.maxMotorTorque = 70;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
return {upper_leg: upper_leg, lower_leg: lower_leg, foot:foot};
}
Walker.prototype.createArm = function() {
// upper arm
this.bd.position.Set(0.5 - this.leg_def.foot_length/2 + this.leg_def.tibia_width/2, this.leg_def.foot_height/2 + this.leg_def.foot_height/2 + this.leg_def.tibia_length + this.leg_def.femur_length + this.torso_def.lower_height + this.torso_def.upper_height - this.arm_def.arm_length/2);
var upper_arm = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.arm_def.arm_width/2, this.arm_def.arm_length/2);
upper_arm.CreateFixture(this.fd);
// lower arm
this.bd.position.Set(0.5 - this.leg_def.foot_length/2 + this.leg_def.tibia_width/2, this.leg_def.foot_height/2 + this.leg_def.foot_height/2 + this.leg_def.tibia_length + this.leg_def.femur_length + this.torso_def.lower_height + this.torso_def.upper_height - this.arm_def.arm_length - this.arm_def.forearm_length/2);
var lower_arm = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.arm_def.forearm_width/2, this.arm_def.forearm_length/2);
lower_arm.CreateFixture(this.fd);
// arm joint
var jd = new b2.RevoluteJointDef();
var position = upper_arm.GetPosition().Clone();
position.y -= this.arm_def.arm_length/2;
jd.Initialize(upper_arm, lower_arm, position);
jd.lowerAngle = 0;
jd.upperAngle = 1.22;
jd.enableLimit = true;
jd.maxMotorTorque = 60;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
return {upper_arm: upper_arm, lower_arm: lower_arm};
}
Walker.prototype.createHead = function() {
// neck
this.bd.position.Set(0.5 - this.leg_def.foot_length/2 + this.leg_def.tibia_width/2, this.leg_def.foot_height/2 + this.leg_def.foot_height/2 + this.leg_def.tibia_length + this.leg_def.femur_length + this.torso_def.lower_height + this.torso_def.upper_height + this.head_def.neck_height/2);
var neck = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.head_def.neck_width/2, this.head_def.neck_height/2);
neck.CreateFixture(this.fd);
// head
this.bd.position.Set(0.5 - this.leg_def.foot_length/2 + this.leg_def.tibia_width/2, this.leg_def.foot_height/2 + this.leg_def.foot_height/2 + this.leg_def.tibia_length + this.leg_def.femur_length + this.torso_def.lower_height + this.torso_def.upper_height + this.head_def.neck_height + this.head_def.head_height/2);
var head = this.world.CreateBody(this.bd);
this.fd.shape.SetAsBox(this.head_def.head_width/2, this.head_def.head_height/2);
head.CreateFixture(this.fd);
// neck joint
var jd = new b2.RevoluteJointDef();
var position = neck.GetPosition().Clone();
position.y += this.head_def.neck_height/2;
jd.Initialize(head, neck, position);
jd.lowerAngle = -0.1;
jd.upperAngle = 0.1;
jd.enableLimit = true;
jd.maxMotorTorque = 2;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
return {head: head, neck: neck};
}
Walker.prototype.connectParts = function() {
// neck/torso
// var jd = new b2.RevoluteJointDef();
// var position = this.head.neck.GetPosition().Clone();
// position.y -= this.head_def.neck_height/2;
// jd.Initialize(this.head.neck, this.torso.upper_torso, position);
// jd.lowerAngle = 0;
// jd.upperAngle = 0.2;
// jd.enableLimit = true;
// jd.maxMotorTorque = 2;
// jd.motorSpeed = 0;
// jd.enableMotor = true;
// this.joints.push(this.world.CreateJoint(jd));
//neck/torso
var jd = new b2.WeldJointDef();
jd.bodyA = this.head.neck;
jd.bodyB = this.torso.upper_torso;
jd.localAnchorA = new b2.Vec2(0, -this.head_def.neck_height/2);
jd.localAnchorB = new b2.Vec2(0, this.torso_def.upper_height/2);
jd.referenceAngle = 0;
this.world.CreateJoint(jd);
// torso/arms
var jd = new b2.RevoluteJointDef();
position = this.torso.upper_torso.GetPosition().Clone();
position.y += this.torso_def.upper_height/2;
jd.Initialize(this.torso.upper_torso, this.right_arm.upper_arm, position);
jd.lowerAngle = -Math.PI/5;
jd.upperAngle = Math.PI/4;
jd.enableLimit = true;
jd.maxMotorTorque = 120;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
var jd = new b2.RevoluteJointDef();
jd.Initialize(this.torso.upper_torso, this.left_arm.upper_arm, position);
jd.lowerAngle = -Math.PI/5;
jd.upperAngle = Math.PI/4;
jd.enableLimit = true;
jd.maxMotorTorque = 120;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
// torso/legs
var jd = new b2.RevoluteJointDef();
position = this.torso.lower_torso.GetPosition().Clone();
position.y -= this.torso_def.lower_height/2;
jd.Initialize(this.torso.lower_torso, this.right_leg.upper_leg, position);
jd.lowerAngle = -Math.PI/8;
jd.upperAngle = Math.PI/7;
jd.enableLimit = true;
jd.maxMotorTorque = 250;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
var jd = new b2.RevoluteJointDef();
jd.Initialize(this.torso.lower_torso, this.left_leg.upper_leg, position);
jd.lowerAngle = -Math.PI/8;
jd.upperAngle = Math.PI/7;
jd.enableLimit = true;
jd.maxMotorTorque = 250;
jd.motorSpeed = 0;
jd.enableMotor = true;
this.joints.push(this.world.CreateJoint(jd));
}
Walker.prototype.getBodies = function() {
return [
this.head.head,
this.head.neck,
this.torso.upper_torso,
this.torso.lower_torso,
this.left_arm.upper_arm,
this.left_arm.lower_arm,
this.right_arm.upper_arm,
this.right_arm.lower_arm,
this.left_leg.upper_leg,
this.left_leg.lower_leg,
this.left_leg.foot,
this.right_leg.upper_leg,
this.right_leg.lower_leg,
this.right_leg.foot
];
}
Walker.prototype.createGenome = function(joints, bodies) {
var genome = [];
for(var k = 0; k < joints.length; k++) {
var gene = new Object();
/*
gene.target_body1 = Math.floor(Math.random() * bodies.length);
gene.target_body2 = Math.floor(Math.random() * bodies.length);
gene.target_body3 = Math.floor(Math.random() * bodies.length);
gene.body_cos_multiplier1 = 6*Math.random() - 3;
gene.body_cos_multiplier2 = 6*Math.random() - 3;
gene.body_cos_multiplier3 = 6*Math.random() - 3;
gene.torso_angle_multiplier = 4*Math.random() -2;
*/
gene.cos_factor = 6*Math.random() - 3;
gene.time_factor = Math.random()/10;
gene.time_shift = Math.random()*Math.PI/2
genome.push(gene);
}
return genome;
}
Walker.prototype.simulationStep = function() {
for(var k = 0; k < this.joints.length; k++) {
/*
var target_body1 = this.bodies[this.genome[k].target_body1];
var target_body2 = this.bodies[this.genome[k].target_body2];
var target_body3 = this.bodies[this.genome[k].target_body3];
var sin1 = Math.sin(target_body1.GetAngle());
var sin2 = Math.sin(target_body2.GetAngle());
var sin3 = Math.sin(target_body3.GetAngle());
this.joints[k].SetMotorSpeed(
this.genome[k].cos_factor * Math.sin(this.genome[k].torso_angle_multiplier*this.torso.upper_torso.GetAngle()) +
this.genome[k].body_cos_multiplier1 * sin1 +
this.genome[k].body_cos_multiplier2 * sin2 +
this.genome[k].body_cos_multiplier3 * sin3
);
*/
this.joints[k].SetMotorSpeed(this.genome[k].cos_factor*Math.cos(this.genome[k].time_shift+this.genome[k].time_factor*globals.step_counter));
}
var oldmax = this.max_distance;
var distance = this.torso.upper_torso.GetPosition().x;
this.max_distance = Math.max(this.max_distance, distance);
// score
this.head_height = this.head.head.GetPosition().y;
this.low_foot_height = Math.min(this.left_leg.foot.GetPosition().y, this.right_leg.foot.GetPosition().y);
var body_delta = this.head_height-this.low_foot_height;
var leg_delta = this.right_leg.foot.GetPosition().x - this.left_leg.foot.GetPosition().x;
if(body_delta > config.min_body_delta) {
this.score += body_delta/50;
if(this.max_distance > oldmax) {
if(Math.abs(leg_delta) > config.min_leg_delta && this.head.head.m_linearVelocity.y > -2) {
if(typeof this.leg_delta_sign == 'undefined') {
this.leg_delta_sign = leg_delta/Math.abs(leg_delta);
} else if(this.leg_delta_sign * leg_delta < 0) {
this.leg_delta_sign = leg_delta/Math.abs(leg_delta);
this.steps++;
this.score += 100;
this.score += this.max_distance;
this.health = config.walker_health;
}
}
}
}
if(config.check_health) {
if(body_delta < config.instadeath_delta) {
this.health = 0;
} else {
this.health--;
}
}
return;
}
Walker.prototype.makeName = function(genome) {
var name = '';
var vowels = ['a','e','i','o','u'];
var space_position = Math.floor(genome.length/2);
for(var k = 0; k < genome.length; k++) {
var sum = 0;
for(var l in genome[k]) {
if(genome[k].hasOwnProperty(l)) {
sum += (genome[k][l]*10);
}
sum = Math.abs(Math.floor(sum));
}
if(k == space_position) {
name += ' ';
}
if(k%2) {
name += vowels[sum%5];
} else {
name += String.fromCharCode(97+sum%26);
}
}
return name;
}