Files
rl_2d_walker.js/js/game.js
T

249 lines
7.3 KiB
JavaScript

var requestAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); };
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: 4,
walker_health: 100,
max_floor_tiles: 30,
round_length: 18000,
min_body_delta: 0,
min_leg_delta: 0.0,
};
globals = {};
chooseQoute = function () {
var qoutes = [
'Play the funky music, robot',
'The origin of funkd',
'The chaos computer club',
'Only the humans that like to dance survived',
'Classic robot dance move - the human',
'First we dance Manhatten, then we dance the world',
'Video of subjects one hour after ingesting substance q1043',
'Red robot redemption',
'Father was a rolling robot',
'Light as a trash can, nimble as a ox',
'Have you tried turning it off and on again?',
'Eurovision 2050',
]
var qoute = qoutes[Math.randi(0,qoutes.length)]
document.getElementById('page_quote').innerText = '"'+qoute+'"'
}
displayProgress = function () {
// TODO show stats
var stats = {
'trainingTime': globals.step_counter / config.simulation_fps,
'meanProgress': globals.walkers.map(w => w.last_position).reduce((s, v) => s + v) / globals.walkers.length,
'meanReward': globals.walkers.map(w => w.reward).reduce((s, v) => s + v) / globals.walkers.length,
'bufferSize': globals.agents[0].brain.buffer.size
}
document.getElementById('stats-prog').innerText = JSON.stringify(stats, null, 2)
}
gameInit = function() {
var joints = 12
var bodyParts = 14
var state = bodyParts * 7
var actions = joints
var input = 2 * state + 1 * actions
globals.brains = {
actor: new window.neurojs.Network.Model([
{ type: 'input', size: input },
{ type: 'fc', size: 60, activation: 'relu' },
{ type: 'noise', sigma: 0.3, delta: 0.1, theta: 0.15 },
{ type: 'fc', size: 40, activation: 'relu' },
{ type: 'noise', sigma: 0.3, delta: 0.1, theta: 0.15 },
{ type: 'fc', size: 40, activation: 'relu' },
{ type: 'fc', size: 40, activation: 'relu', dropout: 0.30 },
// delta represents the equilibrium or mean value supported by fundamentals;
// sigma the degree of volatility around it caused by shocks,
// theta the rate by which these shocks dissipate and the variable reverts towards the mean.
{ type: 'noise', sigma: 0.1, delta: 0.1, theta: 0.15 },
{ type: 'fc', size: actions, activation: 'tanh' },
{ type: 'regression' }
]),
critic: new window.neurojs.Network.Model([
{ type: 'input', size: input + actions },
{ type: 'fc', size: 80, activation: 'relu' },
{ type: 'fc', size: 70, activation: 'relu' },
{ type: 'fc', size: 60, activation: 'relu' },
{ type: 'fc', size: 50, activation: 'relu' },
{ type: 'fc', size: 1 },
{ type: 'regression' }
])
}
globals.brains.shared = new window.neurojs.Shared.ConfigPool()
// this.brains.shared.set('actor', this.brains.actor.newConfiguration())
globals.brains.shared.set('critic', globals.brains.critic.newConfiguration())
chooseQoute()
globals.world = new b2.World(new b2.Vec2(0, -10));
[globals.agents, 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));
globals.display_interval = setInterval(displayProgress, Math.round(380 * 1000 / config.draw_fps));
globals.charts_interval = setInterval(updateCharts, Math.round(380 * 1000 / config.draw_fps));
globals.running = true
requestAnimFrame(loop)
}
loop = function () {
simulationStep()
drawFrame()
if (globals.running) requestAnimFrame(loop); // start next timer
}
resetSimulation = function () {
// turn training off temporarlity to avoid NaN's
updateIfLearning(false)
console.log('resetting walkers')
for(var k = 0; k < config.population_size; k++) {
globals.agents[k].walker = globals.walkers[k] = new Walker(globals.world)
}
setTimeout(()=>updateIfLearning(true), 1000)
}
simulationStep = function () {
globals.step_counter++;
// step world
globals.world.Step(1/config.time_step, config.velocity_iterations, config.position_iterations);
globals.world.ClearForces();
// step agents
populationSimulationStep();
}
updateCharts = function () {
if (globals.agents[0].infos.length) {
if (!globals.charts) {
globals.charts = new Charts()
globals.charts.init(globals.agents)
} else {
globals.charts.update(globals.agents)
}
}
}
// 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) {
var walkers = [];
var agents = []
for(var k = 0; k < config.population_size; k++) {
var agent = new Agent({}, globals)
agents.push(agent);
walkers.push(agent.walker)
}
return [agents, walkers];
}
populationSimulationStep = function() {
for(var k = 0; k < config.population_size; k++) {
globals.agents[k].step()
}
var steps = globals.agents[0].walker.steps
if ((steps!==0) && (0 == steps % config.round_length)) {
resetSimulation()
}
}
function saveAs(dv, name) {
var a;
if (typeof window.downloadAnchor == 'undefined') {
a = window.downloadAnchor = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
} else {
a = window.downloadAnchor
}
var blob = new Blob([dv], { type: 'application/octet-binary' }),
tmpURL = window.URL.createObjectURL(blob);
a.href = tmpURL;
a.download = name;
a.click();
window.URL.revokeObjectURL(tmpURL);
a.href = "";
}
downloadBrain = function (n) {
var ts = (new Date()).toISOString().replace(':','_')
var buf = globals.agents[n].brain.export()
saveAs(new DataView(buf), 'walker_brain_'+n+'_'+ts+'.bin')
};
readBrain = function (buf) {
var input = event.target;
var reader = new FileReader();
reader.onload = function(){
var buffer = reader.result
var imported = window.neurojs.NetOnDisk.readMultiPart(buffer)
for (var i = 0; i < globals.agents.length; i++) {
globals.agents[i].brain.algorithm.actor.set(imported.actor.clone())
globals.agents[i].brain.algorithm.critic.set(imported.critic)
}
};
reader.readAsArrayBuffer(input.files[0]);
};
updateIfLearning = function (value) {
for (var i = 0; i < globals.agents.length; i++) {
globals.agents[i].brain.learning = value
}
};