From 3612a27e1f06202ed78876471ecf52babbefb58c Mon Sep 17 00:00:00 2001 From: Thibault Neveu Date: Wed, 13 Jun 2018 15:59:58 +0100 Subject: [PATCH] Api Doc + Policy exemple working --- .gitignore | 3 +- demo/dist/metacar.min.js | 20 +- demo/webapp/full_city.html | 12 - demo/webapp/level1.html | 4 +- demo/webapp/public/js/level1.js | 25 +- demo/webapp/public/js/policy_agent.js | 286 ++++++++++++++++++ demo/webapp/public/js/utils.js | 13 + .../models/policy-model-policy-agent.json | 1 + .../policy-model-policy-agent.weights.bin | Bin 0 -> 1056 bytes .../policy/policy-model-policy-agent.json | 1 + .../policy-model-policy-agent.weights.bin | Bin 0 -> 1056 bytes .../policy/value-model-policy-agent.json | 1 + .../value-model-policy-agent.weights.bin | Bin 0 -> 976 bytes .../models/value-model-policy-agent.json | 1 + .../value-model-policy-agent.weights.bin | Bin 0 -> 976 bytes dist/metacar.min.js | 20 +- package.json | 4 +- src/basic_motion_engine.ts | 12 +- src/car.ts | 2 +- src/embedded.ts | 15 +- src/level.ts | 5 +- src/metacar.ts | 167 +++------- src/motion_engine.ts | 12 + src/ui_event.ts | 131 ++++++++ typedoc.json | 18 ++ 25 files changed, 579 insertions(+), 174 deletions(-) create mode 100644 demo/webapp/public/js/policy_agent.js create mode 100644 demo/webapp/public/js/utils.js create mode 100644 demo/webapp/public/models/policy-model-policy-agent.json create mode 100644 demo/webapp/public/models/policy-model-policy-agent.weights.bin create mode 100644 demo/webapp/public/models/policy/policy-model-policy-agent.json create mode 100644 demo/webapp/public/models/policy/policy-model-policy-agent.weights.bin create mode 100644 demo/webapp/public/models/policy/value-model-policy-agent.json create mode 100644 demo/webapp/public/models/policy/value-model-policy-agent.weights.bin create mode 100644 demo/webapp/public/models/value-model-policy-agent.json create mode 100644 demo/webapp/public/models/value-model-policy-agent.weights.bin create mode 100644 src/ui_event.ts create mode 100644 typedoc.json diff --git a/.gitignore b/.gitignore index e2acffa..9da8815 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules/* dist/dist-es6/* demo/dist/dist-es6/* demo/node_modules/ -*package-lock.json* \ No newline at end of file +*package-lock.json* +docs \ No newline at end of file diff --git a/demo/dist/metacar.min.js b/demo/dist/metacar.min.js index d3df358..769bff9 100644 --- a/demo/dist/metacar.min.js +++ b/demo/dist/metacar.min.js @@ -107,7 +107,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"BasicMotionEngine\", function() { return BasicMotionEngine; });\n/* harmony import */ var _motion_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./motion_engine */ \"./src/motion_engine.ts\");\n/* harmony import */ var _global__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./global */ \"./src/global.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\nvar __extends = (undefined && undefined.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n\n\n\nvar BasicMotionEngine = (function (_super) {\n __extends(BasicMotionEngine, _super);\n function BasicMotionEngine(level, options) {\n var _this = _super.call(this, level) || this;\n _this.rotationStep = options.rotationStep;\n _this.actions = options.actions;\n return _this;\n }\n BasicMotionEngine.prototype.setUp = function (car, lidar) {\n this.car = car;\n this.lidar = lidar;\n this.state = [];\n for (var y = 0; y < lidar.pts; y++) {\n var line = [];\n for (var x = 0; x < lidar.pts; x++) {\n line.push(_global__WEBPACK_IMPORTED_MODULE_1__[\"MAP\"].DEFAULT);\n }\n this.state.push(line);\n }\n this.setUpKeyboard();\n this.car.v = 0;\n this.detectInteractions();\n };\n BasicMotionEngine.prototype.setUpKeyboard = function () {\n var _this = this;\n var left = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](37);\n var up = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](38);\n var right = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](39);\n var down = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](40);\n if (this.actions.indexOf(\"LEFT\") != -1)\n left.press = function () { _this.turnLeft(); };\n if (this.actions.indexOf(\"RIGHT\") != -1)\n right.press = function () { _this.turnRight(); };\n if (this.actions.indexOf(\"UP\") != -1) {\n up.press = function () { _this.moveForward(); };\n up.release = function () {\n _this.car.v = 0;\n };\n }\n if (this.actions.indexOf(\"DOWN\") != -1) {\n down.press = function () { _this.moveBackward(); };\n down.release = function () {\n _this.car.v = 0;\n };\n }\n };\n BasicMotionEngine.prototype.turnLeft = function () {\n this.car.rotation -= this.rotationStep * Math.PI;\n this.lidar.rotation = this.car.rotation;\n };\n BasicMotionEngine.prototype.turnRight = function () {\n this.car.rotation += this.rotationStep * Math.PI;\n this.lidar.rotation = this.car.rotation;\n };\n BasicMotionEngine.prototype.moveForward = function () {\n this.car.v = 1;\n };\n BasicMotionEngine.prototype.moveBackward = function () {\n this.car.v = -1;\n };\n BasicMotionEngine.prototype.actionStep = function (delta, action) {\n if (this.actions[action] == \"LEFT\") {\n this.turnLeft();\n }\n if (this.actions[action] == \"RIGHT\") {\n this.turnRight();\n }\n if (this.actions[action] == \"UP\") {\n this.moveForward();\n }\n if (this.actions[action] == \"DOWN\") {\n this.moveBackward();\n }\n var _a = this.step(delta), agent_col = _a.agent_col, on_road = _a.on_road;\n this.car.v = 0;\n return { agent_col: agent_col, on_road: on_road };\n };\n BasicMotionEngine.prototype.actionSpace = function () {\n return Array.apply(null, { length: this.actions.length }).map(Number.call, Number);\n };\n BasicMotionEngine.prototype.step = function (delta) {\n this.car.x += this.car.v * Math.cos(this.car.rotation) * delta;\n this.car.y += this.car.v * Math.sin(this.car.rotation) * delta;\n this.car.mx = Math.floor(this.car.x / _global__WEBPACK_IMPORTED_MODULE_1__[\"ROADSIZE\"]);\n this.car.my = Math.floor(this.car.y / _global__WEBPACK_IMPORTED_MODULE_1__[\"ROADSIZE\"]);\n this.car.checkAndsetNewRoad();\n this.lidar.x = this.car.x;\n this.lidar.y = this.car.y;\n this.lidar.rotation = this.car.rotation;\n var _a = this.detectInteractions(), agent_col = _a.agent_col, on_road = _a.on_road;\n if (agent_col.length > 0) {\n this.car.v = 0;\n this.car.vy = 0;\n }\n return { agent_col: agent_col, on_road: on_road };\n };\n return BasicMotionEngine;\n}(_motion_engine__WEBPACK_IMPORTED_MODULE_0__[\"MotionEngine\"]));\n\n\n\n//# sourceURL=webpack://metacar/./src/basic_motion_engine.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"BasicMotionEngine\", function() { return BasicMotionEngine; });\n/* harmony import */ var _motion_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./motion_engine */ \"./src/motion_engine.ts\");\n/* harmony import */ var _global__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./global */ \"./src/global.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\nvar __extends = (undefined && undefined.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n\n\n\nvar BasicMotionEngine = (function (_super) {\n __extends(BasicMotionEngine, _super);\n function BasicMotionEngine(level, options) {\n var _this = _super.call(this, level) || this;\n _this.rotationStep = options.rotationStep;\n _this.actions = options.actions;\n return _this;\n }\n BasicMotionEngine.prototype.setUp = function (car, lidar) {\n this.car = car;\n this.lidar = lidar;\n this.state = [];\n for (var y = 0; y < lidar.pts; y++) {\n var line = [];\n for (var x = 0; x < lidar.pts; x++) {\n line.push(_global__WEBPACK_IMPORTED_MODULE_1__[\"MAP\"].DEFAULT);\n }\n this.state.push(line);\n }\n this.setUpKeyboard();\n this.car.v = 0;\n this.detectInteractions();\n };\n BasicMotionEngine.prototype.setUpKeyboard = function () {\n var _this = this;\n var left = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](37);\n var up = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](38);\n var right = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](39);\n var down = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](40);\n if (this.actions.indexOf(\"LEFT\") != -1)\n left.press = function () { _this.turnLeft(); };\n if (this.actions.indexOf(\"RIGHT\") != -1)\n right.press = function () { _this.turnRight(); };\n if (this.actions.indexOf(\"UP\") != -1) {\n up.press = function () { _this.moveForward(); };\n up.release = function () {\n _this.car.v = 0;\n };\n }\n if (this.actions.indexOf(\"DOWN\") != -1) {\n down.press = function () { _this.moveBackward(); };\n down.release = function () {\n _this.car.v = 0;\n };\n }\n };\n BasicMotionEngine.prototype.turnLeft = function () {\n this.car.rotation -= this.rotationStep * Math.PI;\n this.lidar.rotation = this.car.rotation;\n };\n BasicMotionEngine.prototype.turnRight = function () {\n this.car.rotation += this.rotationStep * Math.PI;\n this.lidar.rotation = this.car.rotation;\n };\n BasicMotionEngine.prototype.moveForward = function () {\n this.car.v = 1;\n };\n BasicMotionEngine.prototype.moveBackward = function () {\n this.car.v = -1;\n };\n BasicMotionEngine.prototype.actionStep = function (delta, action) {\n if (this.actions[action] == \"LEFT\") {\n this.turnLeft();\n }\n if (this.actions[action] == \"RIGHT\") {\n this.turnRight();\n }\n if (this.actions[action] == \"UP\") {\n this.moveForward();\n }\n if (this.actions[action] == \"DOWN\") {\n this.moveBackward();\n }\n var _a = this.step(delta), agent_col = _a.agent_col, on_road = _a.on_road;\n this.car.v = 0;\n return { agent_col: agent_col, on_road: on_road };\n };\n BasicMotionEngine.prototype.actionSpace = function () {\n return {\n type: \"Discrete\",\n size: 1,\n range: [0, 2]\n };\n };\n BasicMotionEngine.prototype.step = function (delta) {\n this.car.x += this.car.v * Math.cos(this.car.rotation) * delta;\n this.car.y += this.car.v * Math.sin(this.car.rotation) * delta;\n this.car.mx = Math.floor(this.car.x / _global__WEBPACK_IMPORTED_MODULE_1__[\"ROADSIZE\"]);\n this.car.my = Math.floor(this.car.y / _global__WEBPACK_IMPORTED_MODULE_1__[\"ROADSIZE\"]);\n this.car.checkAndsetNewRoad();\n this.lidar.x = this.car.x;\n this.lidar.y = this.car.y;\n this.lidar.rotation = this.car.rotation;\n var _a = this.detectInteractions(), agent_col = _a.agent_col, on_road = _a.on_road;\n if (agent_col.length > 0) {\n this.car.v = 0;\n this.car.vy = 0;\n }\n return { agent_col: agent_col, on_road: on_road };\n };\n return BasicMotionEngine;\n}(_motion_engine__WEBPACK_IMPORTED_MODULE_0__[\"MotionEngine\"]));\n\n\n\n//# sourceURL=webpack://metacar/./src/basic_motion_engine.ts?"); /***/ }), @@ -155,7 +155,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"embeddedUrl\", function() { return embeddedUrl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"embeddedContent\", function() { return embeddedContent; });\n/* harmony import */ var _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./embedded/level/full_city */ \"./src/embedded/level/full_city.ts\");\n/* harmony import */ var _embedded_level_level_1__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./embedded/level/level_1 */ \"./src/embedded/level/level_1.ts\");\n\n\nvar embeddedUrl = {\n fullCity: \"embedded://level/fullCity\",\n level1: \"embedded://level/level1\"\n};\nvar embeddedContent = {\n level: {\n fullCity: _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__[\"fullCity\"],\n level1: _embedded_level_level_1__WEBPACK_IMPORTED_MODULE_1__[\"level1\"]\n }\n};\n\n\n//# sourceURL=webpack://metacar/./src/embedded.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"embeddedUrl\", function() { return embeddedUrl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"embeddedContent\", function() { return embeddedContent; });\n/* harmony import */ var _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./embedded/level/full_city */ \"./src/embedded/level/full_city.ts\");\n/* harmony import */ var _embedded_level_level_1__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./embedded/level/level_1 */ \"./src/embedded/level/level_1.ts\");\n\n\n;\nvar embeddedUrl = {\n fullCity: \"embedded://level/fullCity\",\n level1: \"embedded://level/level1\"\n};\nvar embeddedContent = {\n level: {\n fullCity: _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__[\"fullCity\"],\n level1: _embedded_level_level_1__WEBPACK_IMPORTED_MODULE_1__[\"level1\"]\n }\n};\n\n\n//# sourceURL=webpack://metacar/./src/embedded.ts?"); /***/ }), @@ -215,7 +215,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _met /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Level\", function() { return Level; });\n/* harmony import */ var _global__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./global */ \"./src/global.ts\");\n/* harmony import */ var _asset_manager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./asset_manager */ \"./src/asset_manager.ts\");\n\n\nvar Level = (function () {\n function Level(levelContent, canvasId) {\n this.app = null;\n this.info = null;\n this.envs = [];\n this.map = null;\n this.agent = null;\n this.roads = {};\n this.cars = [];\n this.info = levelContent;\n this.map = this.info.map;\n this.canvasId = canvasId;\n this.am = new _asset_manager__WEBPACK_IMPORTED_MODULE_1__[\"AssetManger\"](this);\n }\n Level.prototype.load = function (loop) {\n var _this = this;\n this.loop = loop;\n return new Promise(function (resolve, reject) {\n _this.createLevel(_this.info).then(function () { return resolve(); });\n });\n };\n Level.prototype.render = function (val) {\n if (val) {\n this.app.ticker.start();\n }\n else {\n this.app.ticker.stop();\n }\n };\n Level.prototype.createLevel = function (info) {\n var _this = this;\n this.app = new PIXI.Application({\n width: this.map[0].length * _global__WEBPACK_IMPORTED_MODULE_0__[\"ROADSIZE\"],\n height: this.map.length * _global__WEBPACK_IMPORTED_MODULE_0__[\"ROADSIZE\"],\n backgroundColor: 0x80bf3e\n });\n document.getElementById(this.canvasId).appendChild(this.app.view);\n return new Promise(function (resolve, reject) {\n _global__WEBPACK_IMPORTED_MODULE_0__[\"Loader\"].add([\"public/textures/textures.json\", \"public/textures/textures.png\"]).load(function () {\n _this.setup(info);\n resolve();\n });\n });\n };\n Level.prototype.setup = function (info) {\n var _this = this;\n var textures = _global__WEBPACK_IMPORTED_MODULE_0__[\"Loader\"].resources[_global__WEBPACK_IMPORTED_MODULE_0__[\"JSON_TEXTURES\"]].textures;\n this.am.createMap(this.map, info, textures);\n this.am.createCars(this.map, info, textures);\n if (info.agent)\n this.agent = this.am.createAgent(this.map, info, textures);\n this.app.ticker.add(function (delta) { return _this.loop(delta); });\n };\n Level.prototype.reset = function () {\n this.agent.reset();\n };\n Level.prototype.setReward = function (agent_col, on_road, action) {\n var reward = -0.1;\n if (action == 0 || this.agent.core.vx == 1)\n reward += 0.5;\n if (agent_col.length > 0) {\n reward = -10;\n }\n else if (!on_road) {\n reward = -10;\n }\n return reward;\n };\n Level.prototype.step = function (delta, action) {\n if (action === void 0) { action = null; }\n for (var c = 0; c < this.cars.length; c++) {\n if (this.cars[c].lidar && !this.cars[c].core.agent)\n this.cars[c].step(delta);\n }\n if (this.agent) {\n var _a = this.agent.step(delta, action), agent_col = _a.agent_col, on_road = _a.on_road;\n var reward = this.setReward(agent_col, on_road, action);\n return reward;\n }\n return 0;\n };\n Level.prototype.stopRender = function () {\n this.app.ticker.stop();\n };\n Level.prototype.addChild = function (child) {\n this.app.stage.addChild(child);\n };\n Level.prototype.addRoad = function (road) {\n this.roads[[road.my.toString(), road.mx.toString()].toString()] = road;\n this.envs.push(road);\n this.app.stage.addChild(road);\n };\n Level.prototype.addCar = function (car) {\n this.cars.push(car);\n this.app.stage.addChild(car.core);\n this.envs.push(car.core);\n };\n Level.prototype.getRoad = function (my, mx) {\n return this.roads[[my.toString(), mx.toString()].toString()];\n };\n Level.prototype.getRoads = function () {\n return this.roads;\n };\n Level.prototype.findCarById = function (id) {\n return this.cars.find(function (e) { return e.car_id == id; });\n };\n Level.prototype.getEnvs = function () {\n return this.envs;\n };\n Level.prototype.getMap = function () {\n return this.map;\n };\n return Level;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/level.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Level\", function() { return Level; });\n/* harmony import */ var _global__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./global */ \"./src/global.ts\");\n/* harmony import */ var _asset_manager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./asset_manager */ \"./src/asset_manager.ts\");\n\n\nvar Level = (function () {\n function Level(levelContent, canvasId) {\n this.app = null;\n this.info = null;\n this.envs = [];\n this.map = null;\n this.agent = null;\n this.roads = {};\n this.cars = [];\n this.info = levelContent;\n this.map = this.info.map;\n this.canvasId = canvasId;\n this.am = new _asset_manager__WEBPACK_IMPORTED_MODULE_1__[\"AssetManger\"](this);\n }\n Level.prototype.load = function (loop) {\n var _this = this;\n this.loop = loop;\n return new Promise(function (resolve, reject) {\n _this.createLevel(_this.info).then(function () { return resolve(); });\n });\n };\n Level.prototype.render = function (val) {\n if (val) {\n this.app.ticker.start();\n }\n else {\n this.app.ticker.stop();\n }\n };\n Level.prototype.createLevel = function (info) {\n var _this = this;\n this.app = new PIXI.Application({\n width: this.map[0].length * _global__WEBPACK_IMPORTED_MODULE_0__[\"ROADSIZE\"],\n height: this.map.length * _global__WEBPACK_IMPORTED_MODULE_0__[\"ROADSIZE\"],\n backgroundColor: 0x80bf3e\n });\n document.getElementById(this.canvasId).appendChild(this.app.view);\n return new Promise(function (resolve, reject) {\n _global__WEBPACK_IMPORTED_MODULE_0__[\"Loader\"].add([\"public/textures/textures.json\", \"public/textures/textures.png\"]).load(function () {\n _this.setup(info);\n resolve();\n });\n });\n };\n Level.prototype.setup = function (info) {\n var _this = this;\n var textures = _global__WEBPACK_IMPORTED_MODULE_0__[\"Loader\"].resources[_global__WEBPACK_IMPORTED_MODULE_0__[\"JSON_TEXTURES\"]].textures;\n this.am.createMap(this.map, info, textures);\n this.am.createCars(this.map, info, textures);\n if (info.agent)\n this.agent = this.am.createAgent(this.map, info, textures);\n this.app.ticker.add(function (delta) { return _this.loop(delta); });\n };\n Level.prototype.reset = function () {\n this.agent.reset();\n };\n Level.prototype.setReward = function (agent_col, on_road, action) {\n var reward = -0.1;\n if (action == 0 || this.agent.core.v == 1)\n reward += 0.5;\n if (agent_col.length > 0) {\n reward = -10;\n }\n else if (!on_road) {\n reward = -10;\n }\n return reward;\n };\n Level.prototype.step = function (delta, action) {\n if (action === void 0) { action = null; }\n for (var c = 0; c < this.cars.length; c++) {\n if (this.cars[c].lidar && !this.cars[c].core.agent)\n this.cars[c].step(delta);\n }\n if (this.agent) {\n var _a = this.agent.step(delta, action), agent_col = _a.agent_col, on_road = _a.on_road;\n var reward = this.setReward(agent_col, on_road, action);\n return reward;\n }\n return 0;\n };\n Level.prototype.stopRender = function () {\n this.app.ticker.stop();\n };\n Level.prototype.addChild = function (child) {\n this.app.stage.addChild(child);\n };\n Level.prototype.addRoad = function (road) {\n this.roads[[road.my.toString(), road.mx.toString()].toString()] = road;\n this.envs.push(road);\n this.app.stage.addChild(road);\n };\n Level.prototype.addCar = function (car) {\n this.cars.push(car);\n this.app.stage.addChild(car.core);\n this.envs.push(car.core);\n };\n Level.prototype.getRoad = function (my, mx) {\n return this.roads[[my.toString(), mx.toString()].toString()];\n };\n Level.prototype.getRoads = function () {\n return this.roads;\n };\n Level.prototype.findCarById = function (id) {\n return this.cars.find(function (e) { return e.car_id == id; });\n };\n Level.prototype.getEnvs = function () {\n return this.envs;\n };\n Level.prototype.getMap = function () {\n return this.map;\n };\n return Level;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/level.ts?"); /***/ }), @@ -227,7 +227,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MetaCar\", function() { return MetaCar; });\n/* harmony import */ var _level__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./level */ \"./src/level.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\n\n\nvar MetaCar = (function () {\n function MetaCar(canvasId, levelUrl) {\n var _this = this;\n this.eventList = [\"train\", \"play\", \"stop\", \"reset_env\", \"reset_agent\", \"save\", \"load\"];\n if (!canvasId || this.levelUrl) {\n console.error(\"You must specify the canvasId and the levelUrl\");\n }\n this.isPlaying = false;\n this.canvasId = canvasId;\n this.levelUrl = levelUrl;\n this.eventCallback = [\n function (fc) { return _this.onTrain(fc); },\n function (fc) { return _this.onPlay(fc); },\n function (fc) { return _this.onStop(fc); },\n function (fc) { return _this.onResetEnv(fc); },\n function (fc) { return _this.onResetAgent(fc); },\n function (fc) { return _this.onSave(fc); },\n function (fc, opt) { return _this.onLoad(fc, opt); }\n ];\n var canvas = document.getElementById(canvasId);\n var buttons = document.createElement('div');\n buttons.classList.add(\"metacar_buttons_container\");\n buttons.id = \"metacar_\" + canvasId + \"_buttons_container\";\n canvas.parentNode.insertBefore(buttons, canvas.nextSibling);\n this.buttonsContainer = buttons;\n }\n MetaCar.prototype.load = function (level, agent) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n _utils__WEBPACK_IMPORTED_MODULE_1__[\"loadCustomURL\"](_this.levelUrl, function (content) {\n _this.level = new _level__WEBPACK_IMPORTED_MODULE_0__[\"Level\"](content, _this.canvasId);\n _this.level.load(function (delta) { return _this.loop(delta); });\n resolve();\n });\n });\n };\n MetaCar.prototype._createButton = function (parent, name) {\n var button = document.createElement('button');\n button.classList.add(\"metacar_button_train\");\n button.id = \"metacar_\" + this.canvasId + \"_button_\" + name;\n name = name.replace(/_/g, \" \");\n button.innerHTML = name.charAt(0).toUpperCase() + name.slice(1);\n ;\n parent.appendChild(button);\n return button;\n };\n MetaCar.prototype.onTrain = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"train\");\n button.addEventListener(\"click\", function () {\n _this.render(false);\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onPlay = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"play\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onStop = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"stop\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onResetEnv = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"reset_env\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onResetAgent = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"reset_agent\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onSave = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"save\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onLoad = function (fc, options) {\n var button = this._createButton(this.buttonsContainer, \"load_trained_agent\");\n var input_file = document.createElement('input');\n input_file.type = \"file\";\n input_file.accept = \"*/*\";\n input_file.style.display = \"none\";\n input_file.classList.add(\"metacar_button_input_file\");\n input_file.id = \"metacar_\" + this.canvasId + \"_button_input_file\";\n this.buttonsContainer.appendChild(input_file);\n input_file.addEventListener(\"change\", function (dump) {\n console.log(\"New file to handle\");\n _utils__WEBPACK_IMPORTED_MODULE_1__[\"readDump\"](dump, function (content) {\n if (fc)\n fc(content);\n });\n });\n button.addEventListener(\"click\", function () {\n input_file.click();\n });\n };\n MetaCar.prototype.addEvent = function (eventName, fc, options) {\n var index = this.eventList.indexOf(eventName);\n if (index == -1) {\n console.error(\"The environement does not support this event. Only the following are\\\n avaible:\" + this.eventList);\n }\n var event = this.eventList[index];\n if (event != \"load\") {\n this.eventCallback[index](fc);\n }\n else {\n this.eventCallback[index](fc, options);\n }\n };\n MetaCar.prototype.render = function (val) {\n this.level.render(val);\n };\n MetaCar.prototype.save = function (content, file_name) {\n _utils__WEBPACK_IMPORTED_MODULE_1__[\"saveAs\"](content, file_name);\n };\n MetaCar.prototype.actionSpace = function () {\n return this.level.agent.motion.actionSpace();\n };\n MetaCar.prototype.getState = function () {\n return this.level.agent.getState();\n };\n MetaCar.prototype.step = function (action) {\n return this.level.step(1, action);\n };\n MetaCar.prototype.reset = function () {\n this.level.reset();\n };\n MetaCar.prototype.randomRoadPosition = function () {\n this.level.agent.last_position = [];\n var roads = this.level.getRoads();\n var keys = Object.keys(roads);\n keys.sort(function () { return Math.random() - 0.5; });\n for (var k in keys) {\n var road = roads[keys[k]];\n if (road.cars.length == 0) {\n road.setCarPosition(this.level.agent.core);\n break;\n }\n }\n };\n MetaCar.prototype.loop = function (delta) {\n if (this.isPlaying) {\n this.agent.play(this);\n }\n else {\n this.level.step(delta);\n }\n };\n return MetaCar;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/metacar.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MetaCar\", function() { return MetaCar; });\n/* harmony import */ var _level__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./level */ \"./src/level.ts\");\n/* harmony import */ var _ui_event__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ui_event */ \"./src/ui_event.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\n\n\n\nvar MetaCar = (function () {\n function MetaCar(canvasId, levelUrl) {\n this.eventList = [\"train\", \"play\", \"stop\", \"reset_env\", \"reset_agent\", \"load\"];\n if (!canvasId || this.levelUrl) {\n console.error(\"You must specify the canvasId and the levelUrl\");\n }\n this.canvasId = canvasId;\n this.levelUrl = levelUrl;\n }\n MetaCar.prototype._setEvents = function () {\n var _this = this;\n this.event = new _ui_event__WEBPACK_IMPORTED_MODULE_1__[\"UIEvent\"](this.level, this.canvasId);\n this.eventCallback = [\n function (fc) { return _this.event.onTrain(fc); },\n function (fc) { return _this.event.onPlay(fc); },\n function (fc) { return _this.event.onStop(fc); },\n function (fc) { return _this.event.onResetEnv(fc); },\n function (fc) { return _this.event.onResetAgent(fc); },\n function (fc, opt) { return _this.event.onLoad(fc, opt); }\n ];\n };\n MetaCar.prototype.load = function (level, agent) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n _utils__WEBPACK_IMPORTED_MODULE_2__[\"loadCustomURL\"](_this.levelUrl, function (content) {\n _this.level = new _level__WEBPACK_IMPORTED_MODULE_0__[\"Level\"](content, _this.canvasId);\n _this._setEvents();\n _this.level.load(function (delta) { return _this.loop(delta); });\n resolve();\n });\n });\n };\n MetaCar.prototype.addEvent = function (eventName, fc, options) {\n var index = this.eventList.indexOf(eventName);\n if (index == -1) {\n this.event.onCustomEvent(eventName, fc);\n return;\n }\n var event = this.eventList[index];\n if (event != \"load\") {\n this.eventCallback[index](fc);\n }\n else {\n this.eventCallback[index](fc, options);\n }\n };\n MetaCar.prototype.render = function (val) {\n this.level.render(val);\n };\n MetaCar.prototype.save = function (content, file_name) {\n _utils__WEBPACK_IMPORTED_MODULE_2__[\"saveAs\"](content, file_name);\n };\n MetaCar.prototype.actionSpace = function () {\n return this.level.agent.motion.actionSpace();\n };\n MetaCar.prototype.getState = function () {\n return this.level.agent.getState();\n };\n MetaCar.prototype.step = function (action) {\n return this.level.step(1, action);\n };\n MetaCar.prototype.reset = function () {\n this.level.reset();\n };\n MetaCar.prototype.randomRoadPosition = function () {\n this.level.agent.last_position = [];\n var roads = this.level.getRoads();\n var keys = Object.keys(roads);\n keys.sort(function () { return Math.random() - 0.5; });\n for (var k in keys) {\n var road = roads[keys[k]];\n if (road.cars.length == 0) {\n road.setCarPosition(this.level.agent.core);\n break;\n }\n }\n };\n MetaCar.prototype.loop = function (delta) {\n if (this.event.isPlaying()) {\n this.event.playCallback();\n }\n else {\n this.level.step(delta);\n }\n };\n return MetaCar;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/metacar.ts?"); /***/ }), @@ -243,6 +243,18 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ }), +/***/ "./src/ui_event.ts": +/*!*************************!*\ + !*** ./src/ui_event.ts ***! + \*************************/ +/*! exports provided: UIEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"UIEvent\", function() { return UIEvent; });\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\n\nvar UIEvent = (function () {\n function UIEvent(level, canvasId) {\n this.level = level;\n this.canvasId = this.canvasId;\n var canvas = document.getElementById(canvasId);\n var buttons = document.createElement('div');\n buttons.classList.add(\"metacar_buttons_container\");\n buttons.id = \"metacar_\" + canvasId + \"_buttons_container\";\n canvas.parentNode.insertBefore(buttons, canvas.nextSibling);\n this.buttonsContainer = buttons;\n }\n UIEvent.prototype.isPlaying = function () {\n return this.playing;\n };\n UIEvent.prototype._createButton = function (parent, name) {\n var button = document.createElement('button');\n button.classList.add(\"metacar_button_train\");\n button.id = \"metacar_\" + this.canvasId + \"_button_\" + name;\n name = name.replace(/_/g, \" \");\n button.innerHTML = name.charAt(0).toUpperCase() + name.slice(1);\n ;\n parent.appendChild(button);\n return button;\n };\n UIEvent.prototype.onTrain = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"train\");\n button.addEventListener(\"click\", function () {\n _this.level.render(false);\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onPlay = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"play\");\n button.addEventListener(\"click\", function () {\n if (fc) {\n _this.playing = true;\n _this.playCallback = fc;\n }\n });\n };\n UIEvent.prototype.onStop = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"stop\");\n button.addEventListener(\"click\", function () {\n _this.playing = false;\n _this.playCallback = undefined;\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onResetEnv = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"reset_env\");\n button.addEventListener(\"click\", function () {\n _this.level.reset();\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onResetAgent = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"reset_agent\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onCustomEvent = function (name, fc) {\n var button = this._createButton(this.buttonsContainer, name);\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onLoad = function (fc, options) {\n if (options === void 0) { options = Object(); }\n options.local = options.local || false;\n var button = this._createButton(this.buttonsContainer, \"load_trained_agent\");\n var input_file = document.createElement('input');\n input_file.type = \"file\";\n input_file.accept = \"*/*\";\n input_file.style.display = \"none\";\n input_file.classList.add(\"metacar_button_input_file\");\n input_file.id = \"metacar_\" + this.canvasId + \"_button_input_file\";\n this.buttonsContainer.appendChild(input_file);\n input_file.addEventListener(\"change\", function (dump) {\n _utils__WEBPACK_IMPORTED_MODULE_0__[\"readDump\"](dump, function (content) {\n if (fc)\n fc(content);\n });\n });\n button.addEventListener(\"click\", function () {\n if (options.local) {\n input_file.click();\n }\n else {\n if (fc)\n fc();\n }\n });\n };\n return UIEvent;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/ui_event.ts?"); + +/***/ }), + /***/ "./src/utils.ts": /*!**********************!*\ !*** ./src/utils.ts ***! diff --git a/demo/webapp/full_city.html b/demo/webapp/full_city.html index df876a0..93021a1 100644 --- a/demo/webapp/full_city.html +++ b/demo/webapp/full_city.html @@ -8,18 +8,6 @@
-
- - - - - - - -
-
- -
diff --git a/demo/webapp/level1.html b/demo/webapp/level1.html index 8142c67..8726ac6 100644 --- a/demo/webapp/level1.html +++ b/demo/webapp/level1.html @@ -8,10 +8,12 @@
- + + + diff --git a/demo/webapp/public/js/level1.js b/demo/webapp/public/js/level1.js index 83619b1..b0ac3db 100644 --- a/demo/webapp/public/js/level1.js +++ b/demo/webapp/public/js/level1.js @@ -2,27 +2,22 @@ let levelUrl = metacar.level.level1; // Create the environement (canvasID, levelUrl) var env = new metacar.env("canvas", levelUrl); + +// Create the Policy agent +var agent = new PolicyAgent(env); + + env.load().then(() => { // The level is loaded. Add listernes - env.addEvent("train", () => { - console.log("On train!"); - }); - env.addEvent("play", () => { - console.log("On play!"); - }); - env.addEvent("stop", () => { - console.log("On sotp!"); - }); + env.addEvent("train", () => agent.train()); + env.addEvent("play", () => agent.play()); + env.addEvent("stop", () => agent.stop()); env.addEvent("reset_env", () => { console.log("On reset env!"); }); env.addEvent("reset_agent", () => { console.log("On reset agent"); - }) - env.addEvent("save", () => { - console.log("On save"); - }); - env.addEvent("load", (content) => { - console.log("content", content); }); + env.addEvent("save", () => agent.save()); + env.addEvent("load", () => agent.restore()); }); diff --git a/demo/webapp/public/js/policy_agent.js b/demo/webapp/public/js/policy_agent.js new file mode 100644 index 0000000..6489aef --- /dev/null +++ b/demo/webapp/public/js/policy_agent.js @@ -0,0 +1,286 @@ + +class PolicyAgent { + /* + Policy Agent + */ + + constructor(env) { + // Number of timestep for one episode + this.lidarPts = 5; + this.ttLidarPts = 5*5; + this.actionsNb = 3; + + this.env = env + + // Build the policy model and the value model + this.buildValueFc(); + this.buildPolicy(); + } + + buildValueFc(){ + /* + Build the Value function + @weights (Object) Weights for the layer + */ + const LEARNING_RATE = 0.01; + const value_optimizer = tf.train.adam(LEARNING_RATE); + /* + ----------------------- + ** -- Value Model -- ** + ----------------------- + */ + this.valueModel = tf.sequential(); + // First Hidden Layer + this.valueF1 = tf.layers.dense({ + inputShape: this.ttLidarPts, + units: 9, + kernelInitializer: 'randomNormal', + activation: 'tanh' + }); + this.valueModel.add(this.valueF1); + // Output of the value function + this.valueF2 = tf.layers.dense({ + units: 1, + kernelInitializer: "randomNormal", + activation: 'linear', + inputShape: 9, + }); + this.valueModel.add(this.valueF2); + // Compile the value model + this.valueModel.compile({ + optimizer: value_optimizer, + loss: 'meanSquaredError', + metrics: [], + }); + } + + buildPolicy(){ + /* + Build the policy network + @weights (Object) Weights for the layer + */ + const LEARNING_RATE = 0.01; + this.policy_optimizer = tf.train.adam(LEARNING_RATE); + /* + ----------------------- + ** -- Policy Model -- ** + ----------------------- + */ + this.policyInput = tf.input({shape: [this.ttLidarPts]}); + // First layer + this.policyF1 = tf.layers.dense({ + inputShape: this.ttLidarPts, + units: 9, + kernelInitializer: 'randomNormal', + activation: 'tanh' + }); + // Second layer + this.policyF2 = tf.layers.dense({ + units: this.actionsNb, + kernelInitializer: 'randomNormal', + activation: 'softmax', + inputShape: 9, + }); + // Return the softmax of the policy + this.policyPredict = (state) => { + return tf.tidy(() => { + return this.policyF2.apply(this.policyF1.apply(state)); + }); + } + // Loss function -log(p)*advantages + this.policy_loss = (softmaxs, actions, advantages) => { + return tf.tidy(() => { + const one_hot = tf.oneHot(actions, this.actionsNb); + const log_term = tf.log(tf.sum(tf.mul(softmaxs, one_hot.asType("float32")), 1)); + const loss = tf.mul(tf.scalar(-1), tf.sum(tf.mul(advantages, log_term)) ); + return loss; + }); + } + // Usefull method to get the entropy of the softmax + this.policy_entropy = (softmaxs) => { + return tf.tidy(() => { + return tf.mul(tf.scalar(-1), tf.sum(tf.mul(tf.log(softmaxs), softmaxs))); + }); + } + const output = this.policyF2.apply(this.policyF1.apply(this.policyInput)) + this.policyModel = tf.model({inputs: this.policyInput, outputs: output}); + } + + trainValueFc(inputs, targets, mini_batch_size){ + /* + Train the value model + @inputs (tf.tensor) + @targets (tf.tensor) + @mini_batch_size (Integer) Size of each mini batch + */ + return this.valueModel.fit( + inputs, targets, { + batchSize: mini_batch_size, + epochs: 1 + }); + } + + trainPolicy(states, actions, advantages, batch_size, mini_batch_size){ + /* + Train the policy model + @states (Js array) + @actions (Js array) + @advantages (Js array) + @batch_size (Integer) Size of the batch size + @mini_batch_size (Integer) Size of each mini batch size + */ + for (var b = 0; b < batch_size; b+=mini_batch_size) { + + const tf_states = tf.tensor3d(states.slice(b, b+mini_batch_size)).reshape([-1, this.ttLidarPts]); + const tf_actions = tf.tensor1d(actions.slice(b, b+mini_batch_size), "int32"); + const tf_advantages = tf.tensor1d(advantages.slice(b, b+mini_batch_size)); + + this.policy_optimizer.minimize(() => { + let softmaxs = this.policyPredict(tf_states); + let loss = this.policy_loss(softmaxs, tf_actions, tf_advantages); + return loss; + }); + + tf_states.dispose(); + tf_actions.dispose(); + tf_advantages.dispose(); + } + } + + setDefaultTrainingValues(){ + this.gamma = 0.95; + // Maximum number of step per episode + this.nb_step = 800; + this.mini_batch_size = 200; + this.episodeNb = 250; + } + + save(env){ + /* + Save the network + */ + this.valueModel.save('downloads://value-model-policy-agent'); + this.policyModel.save('downloads://policy-model-policy-agent'); + } + + async restore(){ + /* + Restore the weights of the network + */ + this.valueModel = await tf.loadModel('http://localhost:3000/public/models/policy/value-model-policy-agent.json'); + this.policyModel = await tf.loadModel("http://localhost:3000/public/models/policy/policy-model-policy-agent.json"); + } + + play(){ + tf.tidy(() => { + // Get the current state + const st = tf.tensor2d(this.env.getState(), [this.lidarPts, this.lidarPts]).reshape([1, this.ttLidarPts]); + // Predict the policy + const softmax = this.policyModel.predict(st); + softmax.print(); + // Get the action + const argmax = softmax.argMax(1); + const a = argmax.buffer().values[0]; + + argmax.dispose(); + st.dispose(); + softmax.dispose(); + + this.env.step(a); + }); + } + + stop(){ + /* + We stop the training process (if a training is running) + */ + this.episodeNb = 0; + } + + train(env, it=0){ + if (it == 0) + this.setDefaultTrainingValues(); + if (it >= this.episodeNb){ + this.env.render(true); // Render the canvas again + return; + } + console.log("Training it=", it, "/", this.episodeNb); + + (async () => { + // Get the current state + let reward = 0; + const rewards = []; + const states = []; + const actions = []; + + console.log("---"); + console.time("Exploring"); + for (var step = 0; step < this.nb_step; step++) { + // Get the current state + const array_st = this.env.getState(); + // Convert the state into a tensor + const st = tf.tensor(array_st, [this.lidarPts, this.lidarPts]).reshape([1, this.ttLidarPts]); + // Predict the policy + const softmax = this.policyPredict(st); + // Get the action. Pseudo Random choice. We prefer action with + // Higher probability + const action = randomChoice(softmax.buffer().values); + // Create the next batch + rewards.push(reward); + states.push(array_st); + actions.push(action); + // Stop the episode if the car go out of the road or crash an + // other car + if (reward == -10){ + console.log("Early stop Episode"); + softmax.dispose(); + st.dispose(); + break; + } + softmax.dispose(); + st.dispose(); + // Step in the environement with this action + reward = this.env.step(action); + } + // Size of the next batches && minibatches + const batch_size = rewards.length; + const mini_batch_size = Math.min(this.mini_batch_size, batch_size); + + console.log("Episode duration:", step); + console.log("Mean rewards:", mean(rewards)); + console.timeEnd("Exploring"); + + let advantages = []; + let returns = []; + let G = 0.0; + // Compute the total reward for each state + for (let t = batch_size - 1; t >= 0; t--){ + G = rewards[t] + (this.gamma*G); + returns.push(G); + // Predict the value function for this state + const st = tf.tensor2d(states[t], [this.lidarPts, this.lidarPts]).reshape([1, this.ttLidarPts]); + const Vs = this.valueModel.predict(st); + // Advantage + advantages.push(G - Vs.buffer().values[0]); + st.dispose(); + Vs.dispose(); + } + returns = returns.reverse(); + advantages = advantages.reverse(); + + // Train the value model + const tf_batch_states = tf.tensor3d(states).reshape([batch_size, this.ttLidarPts]); + const tf_value_target = tf.tensor1d(returns); + await this.trainValueFc(tf_batch_states, tf_value_target, mini_batch_size); + tf_batch_states.dispose(); + tf_value_target.dispose(); + // Train the policy model + this.trainPolicy(states, actions, advantages, batch_size, mini_batch_size); + // Set the agent on a new free road + this.env.randomRoadPosition(); + //env.reset(); + // Go to the next episode + this.train(this.env, it+1); + })(); + } +} \ No newline at end of file diff --git a/demo/webapp/public/js/utils.js b/demo/webapp/public/js/utils.js new file mode 100644 index 0000000..0c6a0e1 --- /dev/null +++ b/demo/webapp/public/js/utils.js @@ -0,0 +1,13 @@ + +function randomChoice(p) { + let rnd = p.reduce( (a, b) => a + b ) * Math.random(); + return p.findIndex( a => (rnd -= a) < 0 ); +} + +function mean(array){ + if (array.length == 0) + return null; + var sum = array.reduce(function(a, b) { return a + b; }); + var avg = sum / array.length; + return avg; +} \ No newline at end of file diff --git a/demo/webapp/public/models/policy-model-policy-agent.json b/demo/webapp/public/models/policy-model-policy-agent.json new file mode 100644 index 0000000..5f2819f --- /dev/null +++ b/demo/webapp/public/models/policy-model-policy-agent.json @@ -0,0 +1 @@ +{"modelTopology":{"class_name":"Model","config":{"name":"model1","layers":[{"name":"input1","class_name":"InputLayer","config":{"batch_input_shape":[null,25],"dtype":"float32","sparse":false,"name":"input1"},"inbound_nodes":[]},{"name":"dense_Dense3","class_name":"Dense","config":{"units":9,"activation":"tanh","use_bias":true,"kernel_initializer":{"class_name":"RandomNormal","config":{"mean":0,"stddev":0.05,"seed":null}},"bias_initializer":{"class_name":"Zeros","config":{}},"kernel_regularizer":null,"bias_regularizer":null,"activity_regularizer":null,"kernel_constraint":null,"bias_constraint":null,"name":"dense_Dense3","trainable":true,"batch_input_shape":[null,25],"dtype":"float32"},"inbound_nodes":[[["input1",0,0,{}]]]},{"name":"dense_Dense4","class_name":"Dense","config":{"units":3,"activation":"softmax","use_bias":true,"kernel_initializer":{"class_name":"RandomNormal","config":{"mean":0,"stddev":0.05,"seed":null}},"bias_initializer":{"class_name":"Zeros","config":{}},"kernel_regularizer":null,"bias_regularizer":null,"activity_regularizer":null,"kernel_constraint":null,"bias_constraint":null,"name":"dense_Dense4","trainable":true,"batch_input_shape":[null,9],"dtype":"float32"},"inbound_nodes":[[["dense_Dense3",0,0,{}]]]}],"input_layers":[["input1",0,0]],"output_layers":[["dense_Dense4",0,0]]},"keras_version":"tfjs-layers 0.6.6","backend":"tensor_flow.js"},"weightsManifest":[{"paths":["./policy-model-policy-agent.weights.bin"],"weights":[{"name":"dense_Dense3/kernel","shape":[25,9],"dtype":"float32"},{"name":"dense_Dense3/bias","shape":[9],"dtype":"float32"},{"name":"dense_Dense4/kernel","shape":[9,3],"dtype":"float32"},{"name":"dense_Dense4/bias","shape":[3],"dtype":"float32"}]}]} \ No newline at end of file diff --git a/demo/webapp/public/models/policy-model-policy-agent.weights.bin b/demo/webapp/public/models/policy-model-policy-agent.weights.bin new file mode 100644 index 0000000000000000000000000000000000000000..bdb09df0d832121fc062d8ceb595e114bc524073 GIT binary patch literal 1056 zcma*j>oc5Z7=Up^QD>?1sfmqKOC781zR&#<(lmmMDKVL(>|t_r zZGs+>%+h;*TA^^~9L@ZpKs^;{4h^n}xO2)5#E-He*E0oOGY9E`!ZXOv_hXvc|5gq- zxIy~xDoyC+!-oYCu&Rtl&29h+Y8`RSdqz2HAScz1A+T5YwxBy}l8zKjF?R~2s#wvA zptyTZrC)lC8kD5MdES2YRbC6IE|#J7UJhmzT>zKi7$~v$04}UGW8J_b2&^v0JY~Kh z)v$``aGO>qxg1e%&0bgU?(t}+>~n}1ug8s?J#syyg|465APbxOiK0vo4Xh<}_s25& z$Epd0$Jt=6s}aanvuJSm3?q=}LBaa(H1^!oNqhxgNSGkrWRd(@Yr^o2 zACS%aa_aL&8u9V7rFYW>5One-td(73nz$UO3A~2S4JuITQ4D>&N^ngT!#fhCn!0&` zmCz3l_tryjo&ufyrRe_jIJ7miz-dV@c(_ecf9p}w@Sp>WZj8_s&9_8gE+y|i4xuww zUC^P(2#st1{px9rU6DG__nIZw_H-1q<~D3qY9Cs-nsSRlT2y$g!{z6O++VGC!A#PP zMit*dty3npgMR4i+eb=nzYey`LYNfS zsi!ylRmscg_*i2C^Z6I4uJ0U`hK`Zww_=FC^)?CPvPkr+?o7-#CYW28NWb6K3ck|r zBib|zs#!;BwH8B1_N-tm^C*qCT_OFBRoutSm+aulM%KJBkK2sB!j<;4aV<21{gf}{ z9>%w_b@dV4e1M3JTa06W*^A=*N-VkM1p%jPvjp#r&Y|v2VADj#^ow9U)vccwRK+pL zRB|H$l)_GO_HsKTN(sVYJ04a#t3j64g>9M|47V(Xm2xqhIwJ*r2X|t=y+zZ!Z=sB7 z2Kh}nt|XM>?lT>T)c!<5Ov~WK;2?oE$$?%`B;kRxwPau|gxXK8(E0KAndH>_O3zUd z6IwS$ZtsNQ(s4H&atH_cXGrhO3b7-!k1RHikx{2`h?Dh@{In|CSg}kW&Oaer#SK(= zC!3CYE7TK1<3!9=knUtN94Rd!f@ozR zmojO``bKv_n6Ev1uw8{y&eKQ_d$9b@A#|yGhBe0m*hro+r|ccWgTL8v`UaguT=6n{ dNNvc@L0R)zlQ&E{sHdV>0$r? literal 0 HcmV?d00001 diff --git a/demo/webapp/public/models/policy/policy-model-policy-agent.json b/demo/webapp/public/models/policy/policy-model-policy-agent.json new file mode 100644 index 0000000..5f2819f --- /dev/null +++ b/demo/webapp/public/models/policy/policy-model-policy-agent.json @@ -0,0 +1 @@ +{"modelTopology":{"class_name":"Model","config":{"name":"model1","layers":[{"name":"input1","class_name":"InputLayer","config":{"batch_input_shape":[null,25],"dtype":"float32","sparse":false,"name":"input1"},"inbound_nodes":[]},{"name":"dense_Dense3","class_name":"Dense","config":{"units":9,"activation":"tanh","use_bias":true,"kernel_initializer":{"class_name":"RandomNormal","config":{"mean":0,"stddev":0.05,"seed":null}},"bias_initializer":{"class_name":"Zeros","config":{}},"kernel_regularizer":null,"bias_regularizer":null,"activity_regularizer":null,"kernel_constraint":null,"bias_constraint":null,"name":"dense_Dense3","trainable":true,"batch_input_shape":[null,25],"dtype":"float32"},"inbound_nodes":[[["input1",0,0,{}]]]},{"name":"dense_Dense4","class_name":"Dense","config":{"units":3,"activation":"softmax","use_bias":true,"kernel_initializer":{"class_name":"RandomNormal","config":{"mean":0,"stddev":0.05,"seed":null}},"bias_initializer":{"class_name":"Zeros","config":{}},"kernel_regularizer":null,"bias_regularizer":null,"activity_regularizer":null,"kernel_constraint":null,"bias_constraint":null,"name":"dense_Dense4","trainable":true,"batch_input_shape":[null,9],"dtype":"float32"},"inbound_nodes":[[["dense_Dense3",0,0,{}]]]}],"input_layers":[["input1",0,0]],"output_layers":[["dense_Dense4",0,0]]},"keras_version":"tfjs-layers 0.6.6","backend":"tensor_flow.js"},"weightsManifest":[{"paths":["./policy-model-policy-agent.weights.bin"],"weights":[{"name":"dense_Dense3/kernel","shape":[25,9],"dtype":"float32"},{"name":"dense_Dense3/bias","shape":[9],"dtype":"float32"},{"name":"dense_Dense4/kernel","shape":[9,3],"dtype":"float32"},{"name":"dense_Dense4/bias","shape":[3],"dtype":"float32"}]}]} \ No newline at end of file diff --git a/demo/webapp/public/models/policy/policy-model-policy-agent.weights.bin b/demo/webapp/public/models/policy/policy-model-policy-agent.weights.bin new file mode 100644 index 0000000000000000000000000000000000000000..757ab321951dc32ebe8f1a5d12524d80962be441 GIT binary patch literal 1056 zcmV~$3pCYt8~||dLabY{bW=jf%Sj1`5ZaEm^b;K87L3|Vs_KpvCM=iXY$<9owgU}hvM++U1c_qK{5 z7f)f|M51`lTuQ24DI~da9QI_c2HV=p@Fq8uIt?-!nk~cnkR5d2qL`AEX`p#4aLakBer9n2Xb9F=-5W-Pd_ywaW!Dt4qsr`Zd&aG@l&(lj*6v z01hq4WaEl&xfPDhEbi_W*xGOyms~o8@iR7T-wXL2+lOroi~dD-y&VQ zz|-`EsB&%-?aoE;t+k(^tBiw_X79t1E+hD-_p|!#4+~*b`!gwypQaD1ODN&XE09?9 z1Dp>&3!1Jou&*@<>VmC=H{Tqi9qX&$P zTVg?zaT$7}OCdPp0yw8dgCR~L^jP1bveTRB;8$)mqO5>F8tsJd$~}bCs|$tZo>6!* z*akJrSL;$wNOk`4AL)YGMxn#?HYA5;0Uy{8u8RW&F8x09nl?~QWjpos%+PR>v0$g} zU=dSMC{LbbmFFIV*7_`;)pnkh?R1C8Vpo`vJjK@?{itZ1sh?l*K%ASfmRhD(LHcAU z?k)^s?O_w_^~aGo=h;$d-J1dZh08#5>MhntmAvb;3E2FY$QH_uu`cc|hK4$b+5uvo z?cbxj%79r-jBF~SATo|YS5wgUAs7m9nPqHX{A;LFBqxXw#TRd1ce zj7L^tL)bNJzUNIx4b`}CuPIufqW+2xqJ7?awA!!|OUKFJ~Ie#WVNnS9Y(<|Cc5X!KG^w1 zI2-_LolmzM=FQcER%)v=({{6!5;q}FW~tj~tpulE{(`FA?`T3j45iWe&}=9Mr`AfynMsFL z*M54SB?N+cc;NJP)I4noCQq2rR+I~$nZ&}p#vHbZPSZ;XyP^2Z2k^T~1r4wH3QBL5 zlb7m#r1!qV&0LqGE8iN^Ck^Le>%L)>S+$*7XKV(e-xB?U4evoN>qn;jDwOocGU-!R zqOXn9(dYMmCi0*el!8R0F2oV#o}W|4#kC@jdO5kHUPRJpA@O)2BeE19uJ2G1J^kUk z&>SU&F3(74@{XlTe#c-#T!@nE_fw^vB{>=z%RS6aCwlvGQf0_hr#1wVPgKWX?4&t& zUnb`c=J$~s?qyuC%_*{@qLTPg7rwIiCq7DBj$pu&s(d@SjoZB>^RQEAVB`oN)B7D? zoO+8l-E^5~Q=K3x1JH`3npE4=bH^JmkX@!V{O+3;{FJa3O*9Vis%sJ`$c-T5G0CXV z?HVsx^`&P8VbJX!0*2|Gpg5_d&28CmwMz(9%b|2wyN((?Jy?>rEe?9-gtMQ!;m$k( zGb&hwdDRMx#aXh~>&&oddWrASEg|1HTky&p@mKmhBZ{r_$SeLSy13Lqwk+m>gUFW& zn@n+}n+5*fS&G-prOd+MjwRzZ_?!K-5_m0OtZ8v zpt$HbbCgT)V5tX|h8@JAz2R&<=PmqEdoT`&5i_emAH0(0$8xqmf#gS9aK^wpc;S*e z>vVO&cjY#?p;pLpJ~qa7v!iq}>lTblhQM)CFFY4a(5&lf7)@i)bEut0Pq)D@3u;<~ zTVQG~3pP@;Fh4#-r^bfhUVaN`_$KgO3M`KH|rk4W9T!2ge$%B}~TlV2zg>>lTXe|CScn%>V!Z literal 0 HcmV?d00001 diff --git a/demo/webapp/public/models/value-model-policy-agent.json b/demo/webapp/public/models/value-model-policy-agent.json new file mode 100644 index 0000000..4f8b75a --- /dev/null +++ b/demo/webapp/public/models/value-model-policy-agent.json @@ -0,0 +1 @@ +{"modelTopology":{"class_name":"Sequential","config":[{"class_name":"Dense","config":{"units":9,"activation":"tanh","use_bias":true,"kernel_initializer":{"class_name":"RandomNormal","config":{"mean":0,"stddev":0.05,"seed":null}},"bias_initializer":{"class_name":"Zeros","config":{}},"kernel_regularizer":null,"bias_regularizer":null,"activity_regularizer":null,"kernel_constraint":null,"bias_constraint":null,"name":"dense_Dense1","trainable":true,"batch_input_shape":[null,25],"dtype":"float32"}},{"class_name":"Dense","config":{"units":1,"activation":"linear","use_bias":true,"kernel_initializer":{"class_name":"RandomNormal","config":{"mean":0,"stddev":0.05,"seed":null}},"bias_initializer":{"class_name":"Zeros","config":{}},"kernel_regularizer":null,"bias_regularizer":null,"activity_regularizer":null,"kernel_constraint":null,"bias_constraint":null,"name":"dense_Dense2","trainable":true,"batch_input_shape":[null,9],"dtype":"float32"}}],"keras_version":"tfjs-layers 0.6.6","backend":"tensor_flow.js"},"weightsManifest":[{"paths":["./value-model-policy-agent.weights.bin"],"weights":[{"name":"dense_Dense1/kernel","shape":[25,9],"dtype":"float32"},{"name":"dense_Dense1/bias","shape":[9],"dtype":"float32"},{"name":"dense_Dense2/kernel","shape":[9,1],"dtype":"float32"},{"name":"dense_Dense2/bias","shape":[1],"dtype":"float32"}]}]} \ No newline at end of file diff --git a/demo/webapp/public/models/value-model-policy-agent.weights.bin b/demo/webapp/public/models/value-model-policy-agent.weights.bin new file mode 100644 index 0000000000000000000000000000000000000000..4fb897e623c949378b38dd9329179d016d6d76a9 GIT binary patch literal 976 zcma*fi&GPH8~||T5keq>ND%Qbjxz+6ONa9KeSZNeaD#cGJa&aO5b}znE|3xehHSEK z5EhgRZ*W?-BSnyh+<^VQKMtZXqQQZ~09VFCC9m_vM1-L0Kj`xZe4=`z*}-u+mh_*; z1odSY?RFuGhkdYrcRpCk%h2)O9F`Ek`Xo*Q!kvhlAdu)a#qd?9o;Zz5aU!({?!T$i zwe+gldPoqAWvNMy%?znHp8%saQ)JnAnFXf}6H%gq`P?4D(N7YfCY6#kWsGk7<4aJr zbc1b4&cfVd9dIP&E)h!FS*+ks=Idp`j+!xe8t%=^qzPIoyBoICq6J1*a; zvkZu^Jj@=QY^r(&-L~S&;g=xQV=!{ioJGVPn^9Qh0LyEBm>-i0pIJ4MPA_Ms87b5m zu9z6}Q?j&oYEhg0I-NarjugrUbSmQxxEW!=pBfB!+N}oy%Q|7x;wYL1ov8cUnILoc zfKdBd*z0e>(?KifF?SWhynY6V7m{7!QZnnCfqTMQK@o4qtd4p@r}$TLs*0kmz!ydL zf570~m0;^q4q^QoOk)Z#EHz_$t`yDt1fcS62VJWcC%b&9slg05c?KKQ2yN2MT+(Ji zaoTOLKWs;TA6md^zxQZqPy$T~Rq%|0jr5MJkQY@;xa`RZDqL64BdEa<;{!-}^$J*9 zOYx7@ZZKBv;Rp8m@*A!$)N|F7Zu%{qeV7tT7AoAqdH*yqM`($w=p;6YZ}rUfeav=> z1K~l=m-u<^1WTM!fxziGi+?DE^pJC`_Kg=k%hkh7Z=59hJHuGgI7M>XCgB^UQs?8| z1^pX>P!RJS%kjTXsDCDz471_Q15%KeM$rWiFMi>~5X8J|z)@ib+Db>DZ)O#W3pD8Q z>__^VW*JOF$h|8x@Qb0H|GiDllSai`Idd(GlS{q?$u?dSq@?gfk4viP*n5oo6j>pA8`9e1ataL)!d^0%S zw834~UGVBy|qlC(;Z7(M#}X-!z7Nx(1Jg_FO9-N2@vm)XP_3 z<%=n--?jyI*dg?WRtP?lT!nuu4wyF+go`8AaBqD(^Q)6M{wE)nL6+vEX zzV5(GFj~K8AW|F8KM DcRkNO literal 0 HcmV?d00001 diff --git a/dist/metacar.min.js b/dist/metacar.min.js index d3df358..769bff9 100644 --- a/dist/metacar.min.js +++ b/dist/metacar.min.js @@ -107,7 +107,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"BasicMotionEngine\", function() { return BasicMotionEngine; });\n/* harmony import */ var _motion_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./motion_engine */ \"./src/motion_engine.ts\");\n/* harmony import */ var _global__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./global */ \"./src/global.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\nvar __extends = (undefined && undefined.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n\n\n\nvar BasicMotionEngine = (function (_super) {\n __extends(BasicMotionEngine, _super);\n function BasicMotionEngine(level, options) {\n var _this = _super.call(this, level) || this;\n _this.rotationStep = options.rotationStep;\n _this.actions = options.actions;\n return _this;\n }\n BasicMotionEngine.prototype.setUp = function (car, lidar) {\n this.car = car;\n this.lidar = lidar;\n this.state = [];\n for (var y = 0; y < lidar.pts; y++) {\n var line = [];\n for (var x = 0; x < lidar.pts; x++) {\n line.push(_global__WEBPACK_IMPORTED_MODULE_1__[\"MAP\"].DEFAULT);\n }\n this.state.push(line);\n }\n this.setUpKeyboard();\n this.car.v = 0;\n this.detectInteractions();\n };\n BasicMotionEngine.prototype.setUpKeyboard = function () {\n var _this = this;\n var left = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](37);\n var up = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](38);\n var right = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](39);\n var down = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](40);\n if (this.actions.indexOf(\"LEFT\") != -1)\n left.press = function () { _this.turnLeft(); };\n if (this.actions.indexOf(\"RIGHT\") != -1)\n right.press = function () { _this.turnRight(); };\n if (this.actions.indexOf(\"UP\") != -1) {\n up.press = function () { _this.moveForward(); };\n up.release = function () {\n _this.car.v = 0;\n };\n }\n if (this.actions.indexOf(\"DOWN\") != -1) {\n down.press = function () { _this.moveBackward(); };\n down.release = function () {\n _this.car.v = 0;\n };\n }\n };\n BasicMotionEngine.prototype.turnLeft = function () {\n this.car.rotation -= this.rotationStep * Math.PI;\n this.lidar.rotation = this.car.rotation;\n };\n BasicMotionEngine.prototype.turnRight = function () {\n this.car.rotation += this.rotationStep * Math.PI;\n this.lidar.rotation = this.car.rotation;\n };\n BasicMotionEngine.prototype.moveForward = function () {\n this.car.v = 1;\n };\n BasicMotionEngine.prototype.moveBackward = function () {\n this.car.v = -1;\n };\n BasicMotionEngine.prototype.actionStep = function (delta, action) {\n if (this.actions[action] == \"LEFT\") {\n this.turnLeft();\n }\n if (this.actions[action] == \"RIGHT\") {\n this.turnRight();\n }\n if (this.actions[action] == \"UP\") {\n this.moveForward();\n }\n if (this.actions[action] == \"DOWN\") {\n this.moveBackward();\n }\n var _a = this.step(delta), agent_col = _a.agent_col, on_road = _a.on_road;\n this.car.v = 0;\n return { agent_col: agent_col, on_road: on_road };\n };\n BasicMotionEngine.prototype.actionSpace = function () {\n return Array.apply(null, { length: this.actions.length }).map(Number.call, Number);\n };\n BasicMotionEngine.prototype.step = function (delta) {\n this.car.x += this.car.v * Math.cos(this.car.rotation) * delta;\n this.car.y += this.car.v * Math.sin(this.car.rotation) * delta;\n this.car.mx = Math.floor(this.car.x / _global__WEBPACK_IMPORTED_MODULE_1__[\"ROADSIZE\"]);\n this.car.my = Math.floor(this.car.y / _global__WEBPACK_IMPORTED_MODULE_1__[\"ROADSIZE\"]);\n this.car.checkAndsetNewRoad();\n this.lidar.x = this.car.x;\n this.lidar.y = this.car.y;\n this.lidar.rotation = this.car.rotation;\n var _a = this.detectInteractions(), agent_col = _a.agent_col, on_road = _a.on_road;\n if (agent_col.length > 0) {\n this.car.v = 0;\n this.car.vy = 0;\n }\n return { agent_col: agent_col, on_road: on_road };\n };\n return BasicMotionEngine;\n}(_motion_engine__WEBPACK_IMPORTED_MODULE_0__[\"MotionEngine\"]));\n\n\n\n//# sourceURL=webpack://metacar/./src/basic_motion_engine.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"BasicMotionEngine\", function() { return BasicMotionEngine; });\n/* harmony import */ var _motion_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./motion_engine */ \"./src/motion_engine.ts\");\n/* harmony import */ var _global__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./global */ \"./src/global.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\nvar __extends = (undefined && undefined.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n\n\n\nvar BasicMotionEngine = (function (_super) {\n __extends(BasicMotionEngine, _super);\n function BasicMotionEngine(level, options) {\n var _this = _super.call(this, level) || this;\n _this.rotationStep = options.rotationStep;\n _this.actions = options.actions;\n return _this;\n }\n BasicMotionEngine.prototype.setUp = function (car, lidar) {\n this.car = car;\n this.lidar = lidar;\n this.state = [];\n for (var y = 0; y < lidar.pts; y++) {\n var line = [];\n for (var x = 0; x < lidar.pts; x++) {\n line.push(_global__WEBPACK_IMPORTED_MODULE_1__[\"MAP\"].DEFAULT);\n }\n this.state.push(line);\n }\n this.setUpKeyboard();\n this.car.v = 0;\n this.detectInteractions();\n };\n BasicMotionEngine.prototype.setUpKeyboard = function () {\n var _this = this;\n var left = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](37);\n var up = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](38);\n var right = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](39);\n var down = _utils__WEBPACK_IMPORTED_MODULE_2__[\"keyboard\"](40);\n if (this.actions.indexOf(\"LEFT\") != -1)\n left.press = function () { _this.turnLeft(); };\n if (this.actions.indexOf(\"RIGHT\") != -1)\n right.press = function () { _this.turnRight(); };\n if (this.actions.indexOf(\"UP\") != -1) {\n up.press = function () { _this.moveForward(); };\n up.release = function () {\n _this.car.v = 0;\n };\n }\n if (this.actions.indexOf(\"DOWN\") != -1) {\n down.press = function () { _this.moveBackward(); };\n down.release = function () {\n _this.car.v = 0;\n };\n }\n };\n BasicMotionEngine.prototype.turnLeft = function () {\n this.car.rotation -= this.rotationStep * Math.PI;\n this.lidar.rotation = this.car.rotation;\n };\n BasicMotionEngine.prototype.turnRight = function () {\n this.car.rotation += this.rotationStep * Math.PI;\n this.lidar.rotation = this.car.rotation;\n };\n BasicMotionEngine.prototype.moveForward = function () {\n this.car.v = 1;\n };\n BasicMotionEngine.prototype.moveBackward = function () {\n this.car.v = -1;\n };\n BasicMotionEngine.prototype.actionStep = function (delta, action) {\n if (this.actions[action] == \"LEFT\") {\n this.turnLeft();\n }\n if (this.actions[action] == \"RIGHT\") {\n this.turnRight();\n }\n if (this.actions[action] == \"UP\") {\n this.moveForward();\n }\n if (this.actions[action] == \"DOWN\") {\n this.moveBackward();\n }\n var _a = this.step(delta), agent_col = _a.agent_col, on_road = _a.on_road;\n this.car.v = 0;\n return { agent_col: agent_col, on_road: on_road };\n };\n BasicMotionEngine.prototype.actionSpace = function () {\n return {\n type: \"Discrete\",\n size: 1,\n range: [0, 2]\n };\n };\n BasicMotionEngine.prototype.step = function (delta) {\n this.car.x += this.car.v * Math.cos(this.car.rotation) * delta;\n this.car.y += this.car.v * Math.sin(this.car.rotation) * delta;\n this.car.mx = Math.floor(this.car.x / _global__WEBPACK_IMPORTED_MODULE_1__[\"ROADSIZE\"]);\n this.car.my = Math.floor(this.car.y / _global__WEBPACK_IMPORTED_MODULE_1__[\"ROADSIZE\"]);\n this.car.checkAndsetNewRoad();\n this.lidar.x = this.car.x;\n this.lidar.y = this.car.y;\n this.lidar.rotation = this.car.rotation;\n var _a = this.detectInteractions(), agent_col = _a.agent_col, on_road = _a.on_road;\n if (agent_col.length > 0) {\n this.car.v = 0;\n this.car.vy = 0;\n }\n return { agent_col: agent_col, on_road: on_road };\n };\n return BasicMotionEngine;\n}(_motion_engine__WEBPACK_IMPORTED_MODULE_0__[\"MotionEngine\"]));\n\n\n\n//# sourceURL=webpack://metacar/./src/basic_motion_engine.ts?"); /***/ }), @@ -155,7 +155,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"embeddedUrl\", function() { return embeddedUrl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"embeddedContent\", function() { return embeddedContent; });\n/* harmony import */ var _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./embedded/level/full_city */ \"./src/embedded/level/full_city.ts\");\n/* harmony import */ var _embedded_level_level_1__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./embedded/level/level_1 */ \"./src/embedded/level/level_1.ts\");\n\n\nvar embeddedUrl = {\n fullCity: \"embedded://level/fullCity\",\n level1: \"embedded://level/level1\"\n};\nvar embeddedContent = {\n level: {\n fullCity: _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__[\"fullCity\"],\n level1: _embedded_level_level_1__WEBPACK_IMPORTED_MODULE_1__[\"level1\"]\n }\n};\n\n\n//# sourceURL=webpack://metacar/./src/embedded.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"embeddedUrl\", function() { return embeddedUrl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"embeddedContent\", function() { return embeddedContent; });\n/* harmony import */ var _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./embedded/level/full_city */ \"./src/embedded/level/full_city.ts\");\n/* harmony import */ var _embedded_level_level_1__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./embedded/level/level_1 */ \"./src/embedded/level/level_1.ts\");\n\n\n;\nvar embeddedUrl = {\n fullCity: \"embedded://level/fullCity\",\n level1: \"embedded://level/level1\"\n};\nvar embeddedContent = {\n level: {\n fullCity: _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__[\"fullCity\"],\n level1: _embedded_level_level_1__WEBPACK_IMPORTED_MODULE_1__[\"level1\"]\n }\n};\n\n\n//# sourceURL=webpack://metacar/./src/embedded.ts?"); /***/ }), @@ -215,7 +215,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _met /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Level\", function() { return Level; });\n/* harmony import */ var _global__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./global */ \"./src/global.ts\");\n/* harmony import */ var _asset_manager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./asset_manager */ \"./src/asset_manager.ts\");\n\n\nvar Level = (function () {\n function Level(levelContent, canvasId) {\n this.app = null;\n this.info = null;\n this.envs = [];\n this.map = null;\n this.agent = null;\n this.roads = {};\n this.cars = [];\n this.info = levelContent;\n this.map = this.info.map;\n this.canvasId = canvasId;\n this.am = new _asset_manager__WEBPACK_IMPORTED_MODULE_1__[\"AssetManger\"](this);\n }\n Level.prototype.load = function (loop) {\n var _this = this;\n this.loop = loop;\n return new Promise(function (resolve, reject) {\n _this.createLevel(_this.info).then(function () { return resolve(); });\n });\n };\n Level.prototype.render = function (val) {\n if (val) {\n this.app.ticker.start();\n }\n else {\n this.app.ticker.stop();\n }\n };\n Level.prototype.createLevel = function (info) {\n var _this = this;\n this.app = new PIXI.Application({\n width: this.map[0].length * _global__WEBPACK_IMPORTED_MODULE_0__[\"ROADSIZE\"],\n height: this.map.length * _global__WEBPACK_IMPORTED_MODULE_0__[\"ROADSIZE\"],\n backgroundColor: 0x80bf3e\n });\n document.getElementById(this.canvasId).appendChild(this.app.view);\n return new Promise(function (resolve, reject) {\n _global__WEBPACK_IMPORTED_MODULE_0__[\"Loader\"].add([\"public/textures/textures.json\", \"public/textures/textures.png\"]).load(function () {\n _this.setup(info);\n resolve();\n });\n });\n };\n Level.prototype.setup = function (info) {\n var _this = this;\n var textures = _global__WEBPACK_IMPORTED_MODULE_0__[\"Loader\"].resources[_global__WEBPACK_IMPORTED_MODULE_0__[\"JSON_TEXTURES\"]].textures;\n this.am.createMap(this.map, info, textures);\n this.am.createCars(this.map, info, textures);\n if (info.agent)\n this.agent = this.am.createAgent(this.map, info, textures);\n this.app.ticker.add(function (delta) { return _this.loop(delta); });\n };\n Level.prototype.reset = function () {\n this.agent.reset();\n };\n Level.prototype.setReward = function (agent_col, on_road, action) {\n var reward = -0.1;\n if (action == 0 || this.agent.core.vx == 1)\n reward += 0.5;\n if (agent_col.length > 0) {\n reward = -10;\n }\n else if (!on_road) {\n reward = -10;\n }\n return reward;\n };\n Level.prototype.step = function (delta, action) {\n if (action === void 0) { action = null; }\n for (var c = 0; c < this.cars.length; c++) {\n if (this.cars[c].lidar && !this.cars[c].core.agent)\n this.cars[c].step(delta);\n }\n if (this.agent) {\n var _a = this.agent.step(delta, action), agent_col = _a.agent_col, on_road = _a.on_road;\n var reward = this.setReward(agent_col, on_road, action);\n return reward;\n }\n return 0;\n };\n Level.prototype.stopRender = function () {\n this.app.ticker.stop();\n };\n Level.prototype.addChild = function (child) {\n this.app.stage.addChild(child);\n };\n Level.prototype.addRoad = function (road) {\n this.roads[[road.my.toString(), road.mx.toString()].toString()] = road;\n this.envs.push(road);\n this.app.stage.addChild(road);\n };\n Level.prototype.addCar = function (car) {\n this.cars.push(car);\n this.app.stage.addChild(car.core);\n this.envs.push(car.core);\n };\n Level.prototype.getRoad = function (my, mx) {\n return this.roads[[my.toString(), mx.toString()].toString()];\n };\n Level.prototype.getRoads = function () {\n return this.roads;\n };\n Level.prototype.findCarById = function (id) {\n return this.cars.find(function (e) { return e.car_id == id; });\n };\n Level.prototype.getEnvs = function () {\n return this.envs;\n };\n Level.prototype.getMap = function () {\n return this.map;\n };\n return Level;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/level.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Level\", function() { return Level; });\n/* harmony import */ var _global__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./global */ \"./src/global.ts\");\n/* harmony import */ var _asset_manager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./asset_manager */ \"./src/asset_manager.ts\");\n\n\nvar Level = (function () {\n function Level(levelContent, canvasId) {\n this.app = null;\n this.info = null;\n this.envs = [];\n this.map = null;\n this.agent = null;\n this.roads = {};\n this.cars = [];\n this.info = levelContent;\n this.map = this.info.map;\n this.canvasId = canvasId;\n this.am = new _asset_manager__WEBPACK_IMPORTED_MODULE_1__[\"AssetManger\"](this);\n }\n Level.prototype.load = function (loop) {\n var _this = this;\n this.loop = loop;\n return new Promise(function (resolve, reject) {\n _this.createLevel(_this.info).then(function () { return resolve(); });\n });\n };\n Level.prototype.render = function (val) {\n if (val) {\n this.app.ticker.start();\n }\n else {\n this.app.ticker.stop();\n }\n };\n Level.prototype.createLevel = function (info) {\n var _this = this;\n this.app = new PIXI.Application({\n width: this.map[0].length * _global__WEBPACK_IMPORTED_MODULE_0__[\"ROADSIZE\"],\n height: this.map.length * _global__WEBPACK_IMPORTED_MODULE_0__[\"ROADSIZE\"],\n backgroundColor: 0x80bf3e\n });\n document.getElementById(this.canvasId).appendChild(this.app.view);\n return new Promise(function (resolve, reject) {\n _global__WEBPACK_IMPORTED_MODULE_0__[\"Loader\"].add([\"public/textures/textures.json\", \"public/textures/textures.png\"]).load(function () {\n _this.setup(info);\n resolve();\n });\n });\n };\n Level.prototype.setup = function (info) {\n var _this = this;\n var textures = _global__WEBPACK_IMPORTED_MODULE_0__[\"Loader\"].resources[_global__WEBPACK_IMPORTED_MODULE_0__[\"JSON_TEXTURES\"]].textures;\n this.am.createMap(this.map, info, textures);\n this.am.createCars(this.map, info, textures);\n if (info.agent)\n this.agent = this.am.createAgent(this.map, info, textures);\n this.app.ticker.add(function (delta) { return _this.loop(delta); });\n };\n Level.prototype.reset = function () {\n this.agent.reset();\n };\n Level.prototype.setReward = function (agent_col, on_road, action) {\n var reward = -0.1;\n if (action == 0 || this.agent.core.v == 1)\n reward += 0.5;\n if (agent_col.length > 0) {\n reward = -10;\n }\n else if (!on_road) {\n reward = -10;\n }\n return reward;\n };\n Level.prototype.step = function (delta, action) {\n if (action === void 0) { action = null; }\n for (var c = 0; c < this.cars.length; c++) {\n if (this.cars[c].lidar && !this.cars[c].core.agent)\n this.cars[c].step(delta);\n }\n if (this.agent) {\n var _a = this.agent.step(delta, action), agent_col = _a.agent_col, on_road = _a.on_road;\n var reward = this.setReward(agent_col, on_road, action);\n return reward;\n }\n return 0;\n };\n Level.prototype.stopRender = function () {\n this.app.ticker.stop();\n };\n Level.prototype.addChild = function (child) {\n this.app.stage.addChild(child);\n };\n Level.prototype.addRoad = function (road) {\n this.roads[[road.my.toString(), road.mx.toString()].toString()] = road;\n this.envs.push(road);\n this.app.stage.addChild(road);\n };\n Level.prototype.addCar = function (car) {\n this.cars.push(car);\n this.app.stage.addChild(car.core);\n this.envs.push(car.core);\n };\n Level.prototype.getRoad = function (my, mx) {\n return this.roads[[my.toString(), mx.toString()].toString()];\n };\n Level.prototype.getRoads = function () {\n return this.roads;\n };\n Level.prototype.findCarById = function (id) {\n return this.cars.find(function (e) { return e.car_id == id; });\n };\n Level.prototype.getEnvs = function () {\n return this.envs;\n };\n Level.prototype.getMap = function () {\n return this.map;\n };\n return Level;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/level.ts?"); /***/ }), @@ -227,7 +227,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MetaCar\", function() { return MetaCar; });\n/* harmony import */ var _level__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./level */ \"./src/level.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\n\n\nvar MetaCar = (function () {\n function MetaCar(canvasId, levelUrl) {\n var _this = this;\n this.eventList = [\"train\", \"play\", \"stop\", \"reset_env\", \"reset_agent\", \"save\", \"load\"];\n if (!canvasId || this.levelUrl) {\n console.error(\"You must specify the canvasId and the levelUrl\");\n }\n this.isPlaying = false;\n this.canvasId = canvasId;\n this.levelUrl = levelUrl;\n this.eventCallback = [\n function (fc) { return _this.onTrain(fc); },\n function (fc) { return _this.onPlay(fc); },\n function (fc) { return _this.onStop(fc); },\n function (fc) { return _this.onResetEnv(fc); },\n function (fc) { return _this.onResetAgent(fc); },\n function (fc) { return _this.onSave(fc); },\n function (fc, opt) { return _this.onLoad(fc, opt); }\n ];\n var canvas = document.getElementById(canvasId);\n var buttons = document.createElement('div');\n buttons.classList.add(\"metacar_buttons_container\");\n buttons.id = \"metacar_\" + canvasId + \"_buttons_container\";\n canvas.parentNode.insertBefore(buttons, canvas.nextSibling);\n this.buttonsContainer = buttons;\n }\n MetaCar.prototype.load = function (level, agent) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n _utils__WEBPACK_IMPORTED_MODULE_1__[\"loadCustomURL\"](_this.levelUrl, function (content) {\n _this.level = new _level__WEBPACK_IMPORTED_MODULE_0__[\"Level\"](content, _this.canvasId);\n _this.level.load(function (delta) { return _this.loop(delta); });\n resolve();\n });\n });\n };\n MetaCar.prototype._createButton = function (parent, name) {\n var button = document.createElement('button');\n button.classList.add(\"metacar_button_train\");\n button.id = \"metacar_\" + this.canvasId + \"_button_\" + name;\n name = name.replace(/_/g, \" \");\n button.innerHTML = name.charAt(0).toUpperCase() + name.slice(1);\n ;\n parent.appendChild(button);\n return button;\n };\n MetaCar.prototype.onTrain = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"train\");\n button.addEventListener(\"click\", function () {\n _this.render(false);\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onPlay = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"play\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onStop = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"stop\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onResetEnv = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"reset_env\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onResetAgent = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"reset_agent\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onSave = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"save\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n MetaCar.prototype.onLoad = function (fc, options) {\n var button = this._createButton(this.buttonsContainer, \"load_trained_agent\");\n var input_file = document.createElement('input');\n input_file.type = \"file\";\n input_file.accept = \"*/*\";\n input_file.style.display = \"none\";\n input_file.classList.add(\"metacar_button_input_file\");\n input_file.id = \"metacar_\" + this.canvasId + \"_button_input_file\";\n this.buttonsContainer.appendChild(input_file);\n input_file.addEventListener(\"change\", function (dump) {\n console.log(\"New file to handle\");\n _utils__WEBPACK_IMPORTED_MODULE_1__[\"readDump\"](dump, function (content) {\n if (fc)\n fc(content);\n });\n });\n button.addEventListener(\"click\", function () {\n input_file.click();\n });\n };\n MetaCar.prototype.addEvent = function (eventName, fc, options) {\n var index = this.eventList.indexOf(eventName);\n if (index == -1) {\n console.error(\"The environement does not support this event. Only the following are\\\n avaible:\" + this.eventList);\n }\n var event = this.eventList[index];\n if (event != \"load\") {\n this.eventCallback[index](fc);\n }\n else {\n this.eventCallback[index](fc, options);\n }\n };\n MetaCar.prototype.render = function (val) {\n this.level.render(val);\n };\n MetaCar.prototype.save = function (content, file_name) {\n _utils__WEBPACK_IMPORTED_MODULE_1__[\"saveAs\"](content, file_name);\n };\n MetaCar.prototype.actionSpace = function () {\n return this.level.agent.motion.actionSpace();\n };\n MetaCar.prototype.getState = function () {\n return this.level.agent.getState();\n };\n MetaCar.prototype.step = function (action) {\n return this.level.step(1, action);\n };\n MetaCar.prototype.reset = function () {\n this.level.reset();\n };\n MetaCar.prototype.randomRoadPosition = function () {\n this.level.agent.last_position = [];\n var roads = this.level.getRoads();\n var keys = Object.keys(roads);\n keys.sort(function () { return Math.random() - 0.5; });\n for (var k in keys) {\n var road = roads[keys[k]];\n if (road.cars.length == 0) {\n road.setCarPosition(this.level.agent.core);\n break;\n }\n }\n };\n MetaCar.prototype.loop = function (delta) {\n if (this.isPlaying) {\n this.agent.play(this);\n }\n else {\n this.level.step(delta);\n }\n };\n return MetaCar;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/metacar.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MetaCar\", function() { return MetaCar; });\n/* harmony import */ var _level__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./level */ \"./src/level.ts\");\n/* harmony import */ var _ui_event__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ui_event */ \"./src/ui_event.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\n\n\n\nvar MetaCar = (function () {\n function MetaCar(canvasId, levelUrl) {\n this.eventList = [\"train\", \"play\", \"stop\", \"reset_env\", \"reset_agent\", \"load\"];\n if (!canvasId || this.levelUrl) {\n console.error(\"You must specify the canvasId and the levelUrl\");\n }\n this.canvasId = canvasId;\n this.levelUrl = levelUrl;\n }\n MetaCar.prototype._setEvents = function () {\n var _this = this;\n this.event = new _ui_event__WEBPACK_IMPORTED_MODULE_1__[\"UIEvent\"](this.level, this.canvasId);\n this.eventCallback = [\n function (fc) { return _this.event.onTrain(fc); },\n function (fc) { return _this.event.onPlay(fc); },\n function (fc) { return _this.event.onStop(fc); },\n function (fc) { return _this.event.onResetEnv(fc); },\n function (fc) { return _this.event.onResetAgent(fc); },\n function (fc, opt) { return _this.event.onLoad(fc, opt); }\n ];\n };\n MetaCar.prototype.load = function (level, agent) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n _utils__WEBPACK_IMPORTED_MODULE_2__[\"loadCustomURL\"](_this.levelUrl, function (content) {\n _this.level = new _level__WEBPACK_IMPORTED_MODULE_0__[\"Level\"](content, _this.canvasId);\n _this._setEvents();\n _this.level.load(function (delta) { return _this.loop(delta); });\n resolve();\n });\n });\n };\n MetaCar.prototype.addEvent = function (eventName, fc, options) {\n var index = this.eventList.indexOf(eventName);\n if (index == -1) {\n this.event.onCustomEvent(eventName, fc);\n return;\n }\n var event = this.eventList[index];\n if (event != \"load\") {\n this.eventCallback[index](fc);\n }\n else {\n this.eventCallback[index](fc, options);\n }\n };\n MetaCar.prototype.render = function (val) {\n this.level.render(val);\n };\n MetaCar.prototype.save = function (content, file_name) {\n _utils__WEBPACK_IMPORTED_MODULE_2__[\"saveAs\"](content, file_name);\n };\n MetaCar.prototype.actionSpace = function () {\n return this.level.agent.motion.actionSpace();\n };\n MetaCar.prototype.getState = function () {\n return this.level.agent.getState();\n };\n MetaCar.prototype.step = function (action) {\n return this.level.step(1, action);\n };\n MetaCar.prototype.reset = function () {\n this.level.reset();\n };\n MetaCar.prototype.randomRoadPosition = function () {\n this.level.agent.last_position = [];\n var roads = this.level.getRoads();\n var keys = Object.keys(roads);\n keys.sort(function () { return Math.random() - 0.5; });\n for (var k in keys) {\n var road = roads[keys[k]];\n if (road.cars.length == 0) {\n road.setCarPosition(this.level.agent.core);\n break;\n }\n }\n };\n MetaCar.prototype.loop = function (delta) {\n if (this.event.isPlaying()) {\n this.event.playCallback();\n }\n else {\n this.level.step(delta);\n }\n };\n return MetaCar;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/metacar.ts?"); /***/ }), @@ -243,6 +243,18 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ }), +/***/ "./src/ui_event.ts": +/*!*************************!*\ + !*** ./src/ui_event.ts ***! + \*************************/ +/*! exports provided: UIEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"UIEvent\", function() { return UIEvent; });\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\n\nvar UIEvent = (function () {\n function UIEvent(level, canvasId) {\n this.level = level;\n this.canvasId = this.canvasId;\n var canvas = document.getElementById(canvasId);\n var buttons = document.createElement('div');\n buttons.classList.add(\"metacar_buttons_container\");\n buttons.id = \"metacar_\" + canvasId + \"_buttons_container\";\n canvas.parentNode.insertBefore(buttons, canvas.nextSibling);\n this.buttonsContainer = buttons;\n }\n UIEvent.prototype.isPlaying = function () {\n return this.playing;\n };\n UIEvent.prototype._createButton = function (parent, name) {\n var button = document.createElement('button');\n button.classList.add(\"metacar_button_train\");\n button.id = \"metacar_\" + this.canvasId + \"_button_\" + name;\n name = name.replace(/_/g, \" \");\n button.innerHTML = name.charAt(0).toUpperCase() + name.slice(1);\n ;\n parent.appendChild(button);\n return button;\n };\n UIEvent.prototype.onTrain = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"train\");\n button.addEventListener(\"click\", function () {\n _this.level.render(false);\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onPlay = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"play\");\n button.addEventListener(\"click\", function () {\n if (fc) {\n _this.playing = true;\n _this.playCallback = fc;\n }\n });\n };\n UIEvent.prototype.onStop = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"stop\");\n button.addEventListener(\"click\", function () {\n _this.playing = false;\n _this.playCallback = undefined;\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onResetEnv = function (fc) {\n var _this = this;\n var button = this._createButton(this.buttonsContainer, \"reset_env\");\n button.addEventListener(\"click\", function () {\n _this.level.reset();\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onResetAgent = function (fc) {\n var button = this._createButton(this.buttonsContainer, \"reset_agent\");\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onCustomEvent = function (name, fc) {\n var button = this._createButton(this.buttonsContainer, name);\n button.addEventListener(\"click\", function () {\n if (fc)\n fc();\n });\n };\n UIEvent.prototype.onLoad = function (fc, options) {\n if (options === void 0) { options = Object(); }\n options.local = options.local || false;\n var button = this._createButton(this.buttonsContainer, \"load_trained_agent\");\n var input_file = document.createElement('input');\n input_file.type = \"file\";\n input_file.accept = \"*/*\";\n input_file.style.display = \"none\";\n input_file.classList.add(\"metacar_button_input_file\");\n input_file.id = \"metacar_\" + this.canvasId + \"_button_input_file\";\n this.buttonsContainer.appendChild(input_file);\n input_file.addEventListener(\"change\", function (dump) {\n _utils__WEBPACK_IMPORTED_MODULE_0__[\"readDump\"](dump, function (content) {\n if (fc)\n fc(content);\n });\n });\n button.addEventListener(\"click\", function () {\n if (options.local) {\n input_file.click();\n }\n else {\n if (fc)\n fc();\n }\n });\n };\n return UIEvent;\n}());\n\n\n\n//# sourceURL=webpack://metacar/./src/ui_event.ts?"); + +/***/ }), + /***/ "./src/utils.ts": /*!**********************!*\ !*** ./src/utils.ts ***! diff --git a/package.json b/package.json index c1a7d3b..d9b603a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "license": "MIT", "devDependencies": { "ts-loader": "^4.3.1", + "typedoc": "^0.11.1", "typescript": "^2.9.1", "webpack": "^4.10.2", "webpack-cli": "^3.0.2" @@ -18,6 +19,7 @@ "scripts": { "build": "./node_modules/.bin/webpack-cli --mode production", "build-dev": "./node_modules/.bin/webpack-cli --mode development", - "watch": "./node_modules/.bin/webpack-cli --watch --mode development" + "watch": "./node_modules/.bin/webpack-cli --watch --mode development", + "docs": "./node_modules/.bin/typedoc --options typedoc.json" } } diff --git a/src/basic_motion_engine.ts b/src/basic_motion_engine.ts index fa337d0..c18de42 100644 --- a/src/basic_motion_engine.ts +++ b/src/basic_motion_engine.ts @@ -6,6 +6,7 @@ import { } from "./global"; import * as U from "./utils"; +import {actionSpaceDescription} from "./motion_engine"; export class BasicMotionEngine extends MotionEngine { /* @@ -123,12 +124,15 @@ export class BasicMotionEngine extends MotionEngine { return {agent_col, on_road}; } - actionSpace(){ + actionSpace(): actionSpaceDescription{ /* - Return an array with all possibles actions - Ex: [0, 1, 2] + Return a description of the action space. */ - return Array.apply(null, {length: this.actions.length}).map(Number.call, Number); + return { + type: "Discrete", + size: 1, + range: [0, 2] + } } step(delta: number){ diff --git a/src/car.ts b/src/car.ts index c0f85d4..ca66d4f 100644 --- a/src/car.ts +++ b/src/car.ts @@ -173,7 +173,7 @@ export class Car { } } - getState(){ + getState(): number[][]{ /* Get the current state of the car The state is the current value of each point diff --git a/src/embedded.ts b/src/embedded.ts index 04da286..0fb665f 100644 --- a/src/embedded.ts +++ b/src/embedded.ts @@ -1,7 +1,20 @@ import {fullCity} from "./embedded/level/full_city"; import {level1} from "./embedded/level/level_1"; -export const embeddedUrl: any = { +/** + * Object used to enumerate each + * level embedded into the library. + * + * @fullCity: A level to show the current capabilities of the environement. + * @level1: A level with one agent, two cars, and simple control (top, down, left, right). + * +*/ +export interface embeddedUrlI { + fullCity: string + level1: string +}; + +export const embeddedUrl: embeddedUrlI = { fullCity: "embedded://level/fullCity", level1: "embedded://level/level1" } diff --git a/src/level.ts b/src/level.ts index 2f6654a..4b766ab 100644 --- a/src/level.ts +++ b/src/level.ts @@ -1,5 +1,4 @@ /* - @Level class This is the core of game, the class is used create all the differents services in the game (assets, map, agents...). */ @@ -122,7 +121,7 @@ export class Level { TODO: Let's the reward define in the agent class */ let reward = -0.1; - if (action == 0 || this.agent.core.vx == 1) + if (action == 0 || this.agent.core.v == 1) reward += 0.5; if (agent_col.length > 0){ reward = -10; @@ -193,7 +192,7 @@ export class Level { getRoads(){ return this.roads; } - + findCarById(id: number){ /* Find car by @id diff --git a/src/metacar.ts b/src/metacar.ts index 60e7cec..e5e7593 100644 --- a/src/metacar.ts +++ b/src/metacar.ts @@ -3,22 +3,23 @@ */ import {Level, LevelInfo} from "./level"; +import {actionSpaceDescription} from "./motion_engine"; +import {UIEvent} from "./ui_event"; import * as U from "./utils"; export interface eventLoadOptions { - computer: boolean; + local: boolean; } export class MetaCar { - private isPlaying: boolean; private agent: any; private level: Level; private canvasId: string; private levelUrl: string; - private eventList: string[] = ["train", "play", "stop", "reset_env", "reset_agent", "save", "load"] + private eventList: string[] = ["train", "play", "stop", "reset_env", "reset_agent", "load"] private eventCallback: any[]; - private buttonsContainer: HTMLDivElement; + private event: UIEvent; constructor(canvasId: string, levelUrl: string) { /** @@ -31,26 +32,21 @@ export class MetaCar { if (!canvasId || this.levelUrl){ console.error("You must specify the canvasId and the levelUrl"); } - this.isPlaying = false; this.canvasId = canvasId; this.levelUrl = levelUrl; - this.eventCallback = [ - (fc: any) => this.onTrain(fc), - (fc: any) => this.onPlay(fc), - (fc:any) => this.onStop(fc), - (fc: any) => this.onResetEnv(fc), - (fc: any) => this.onResetAgent(fc), - (fc: any) => this.onSave(fc), - (fc: any, opt: eventLoadOptions) => this.onLoad(fc, opt) - ]; + } - // Insert the event div - var canvas = document.getElementById(canvasId); - var buttons = document.createElement('div'); // create new textarea - buttons.classList.add("metacar_buttons_container"); - buttons.id = "metacar_"+ canvasId + "_buttons_container"; - canvas.parentNode.insertBefore(buttons, canvas.nextSibling); - this.buttonsContainer = buttons; + private _setEvents(){ + // SetEvents callback + this.event = new UIEvent(this.level, this.canvasId); + this.eventCallback = [ + (fc: any) => this.event.onTrain(fc), + (fc: any) => this.event.onPlay(fc), + (fc:any) => this.event.onStop(fc), + (fc: any) => this.event.onResetEnv(fc), + (fc: any) => this.event.onResetAgent(fc), + (fc: any, opt: eventLoadOptions) => this.event.onLoad(fc, opt) + ]; } load(level: string, agent: any): Promise{ @@ -62,7 +58,8 @@ export class MetaCar { return new Promise((resolve, reject) => { U.loadCustomURL(this.levelUrl, (content: LevelInfo) => { - this.level = new Level(content, this.canvasId); + this.level = new Level(content, this.canvasId); + this._setEvents(); this.level.load((delta: number) => this.loop(delta)); resolve(); }); @@ -97,103 +94,19 @@ export class MetaCar { }); */ } - - private _createButton(parent: HTMLDivElement, name: string): HTMLButtonElement{ - var button = document.createElement('button'); // create new textarea - button.classList.add("metacar_button_train"); - button.id = "metacar_"+ this.canvasId + "_button_" + name; - // Uppercase first letter and replace _ - name = name.replace(/_/g , " "); - button.innerHTML = name.charAt(0).toUpperCase() + name.slice(1);; - parent.appendChild(button); - return button - } - - onTrain(fc: any){ - // Create the button - const button = this._createButton(this.buttonsContainer, "train"); - // Listen the event - button.addEventListener("click", () => { - this.render(false); - if (fc) fc(); - }); - } - - onPlay(fc: any){ - // Create the button - const button = this._createButton(this.buttonsContainer, "play"); - // Listen the event - button.addEventListener("click", () => { - if (fc) fc(); - }); - } - - onStop(fc: any){ - // Create the button - const button = this._createButton(this.buttonsContainer, "stop"); - // Listen the event - button.addEventListener("click", () => { - if (fc) fc(); - }); - } - - onResetEnv(fc: any){ - // Create the button - const button = this._createButton(this.buttonsContainer, "reset_env"); - button.addEventListener("click", () => { - if (fc) fc(); - }); - } - - onResetAgent(fc: any){ - // Create the button - const button = this._createButton(this.buttonsContainer, "reset_agent"); - button.addEventListener("click", () => { - if (fc) fc(); - }); - } - - onSave(fc: any){ - // Create the button - const button = this._createButton(this.buttonsContainer, "save"); - button.addEventListener("click", () => { - if (fc) fc(); - }); - } - - onLoad(fc: any, options: eventLoadOptions){ - // Create the button - const button = this._createButton(this.buttonsContainer, "load_trained_agent"); - // Create the fake input input file - var input_file = document.createElement('input'); // create new textarea - input_file.type = "file"; - input_file.accept = "*/*"; - input_file.style.display = "none"; - input_file.classList.add("metacar_button_input_file"); - input_file.id = "metacar_"+ this.canvasId + "_button_input_file"; - this.buttonsContainer.appendChild(input_file); - - input_file.addEventListener("change", (dump) => { - console.log("New file to handle"); - U.readDump(dump, (content: any) => { - if (fc) fc(content); - }); - }); - - button.addEventListener("click", () => { - input_file.click(); - }); - } - + + /** + * This method is used to add button under the canvas. When a + * click is detected on the window, the associated @fc is called. + * Some events are recognized by the environement, others can be custom. + * @eventName Name of the event to listen. + * @fc Function to call each time this event is raised. + */ addEvent(eventName: string, fc: any, options?: eventLoadOptions):void { - /** - * eventName: Name of the event to listen - * fc: Function to call each time this event is raise - */ const index = this.eventList.indexOf(eventName); if (index == -1){ - console.error("The environement does not support this event. Only the following are\ - avaible:" + this.eventList); + this.event.onCustomEvent(eventName, fc); + return; } const event = this.eventList[index]; if (event != "load"){ @@ -218,18 +131,18 @@ export class MetaCar { U.saveAs(content, file_name); } - actionSpace(){ - /* - Get the possible action to do in the environement - Ex: [0, 1, 2] - */ + /** + * Get the action space of the environement + */ + actionSpace(): actionSpaceDescription{ return this.level.agent.motion.actionSpace(); } - getState(){ - /* - Get the state of this environement - */ + /** + * Return the current state of the environement. + * The size of the state depends of the size of the Lidar. + */ + getState(): number[][]{ return this.level.agent.getState(); } @@ -266,8 +179,8 @@ export class MetaCar { } loop(delta: number){ - if (this.isPlaying){ - this.agent.play(this); + if (this.event.isPlaying()){ + this.event.playCallback(); } else { this.level.step(delta); diff --git a/src/motion_engine.ts b/src/motion_engine.ts index 0ff9efc..d7f403b 100644 --- a/src/motion_engine.ts +++ b/src/motion_engine.ts @@ -15,6 +15,18 @@ export interface MotionOption{ readonly actions: string[]; } +/** + * Structure used to describe the action space. + * @type: Discrete or continous values + * @size: Number of expected values. + * @range: Range of each values +*/ +export interface actionSpaceDescription { + type: "Discrete"|"Continous" + size: number, + range: number[] +} + export class MotionEngine { protected level: Level|Editor; diff --git a/src/ui_event.ts b/src/ui_event.ts new file mode 100644 index 0000000..64c08e3 --- /dev/null +++ b/src/ui_event.ts @@ -0,0 +1,131 @@ +/** + * Event class +*/ + +import {Level} from "./level"; +import {eventLoadOptions} from "./metacar"; +import * as U from "./utils"; + +export class UIEvent { + + private playing: boolean; + private canvasId: string; + private buttonsContainer: HTMLDivElement; + private level: Level; + + public playCallback: any; + + constructor(level: Level, canvasId: string){ + this.level = level; + this.canvasId = this.canvasId; + // Insert the event div + var canvas = document.getElementById(canvasId); + var buttons = document.createElement('div'); // create new textarea + buttons.classList.add("metacar_buttons_container"); + buttons.id = "metacar_"+ canvasId + "_buttons_container"; + canvas.parentNode.insertBefore(buttons, canvas.nextSibling); + this.buttonsContainer = buttons; + } + + public isPlaying(): boolean{ + return this.playing; + } + + private _createButton(parent: HTMLDivElement, name: string): HTMLButtonElement{ + var button = document.createElement('button'); // create new textarea + button.classList.add("metacar_button_train"); + button.id = "metacar_"+ this.canvasId + "_button_" + name; + // Uppercase first letter and replace _ + name = name.replace(/_/g , " "); + button.innerHTML = name.charAt(0).toUpperCase() + name.slice(1);; + parent.appendChild(button); + return button + } + + public onTrain(fc: any){ + // Create the button + const button = this._createButton(this.buttonsContainer, "train"); + // Listen the event + button.addEventListener("click", () => { + this.level.render(false); + if (fc) fc(); + }); + } + + public onPlay(fc: any){ + // Create the button + const button = this._createButton(this.buttonsContainer, "play"); + // Listen the event + button.addEventListener("click", () => { + if (fc) { + this.playing = true; + this.playCallback = fc; + } + }); + } + + public onStop(fc: any){ + // Create the button + const button = this._createButton(this.buttonsContainer, "stop"); + // Listen the event + button.addEventListener("click", () => { + this.playing = false; + this.playCallback = undefined; + if (fc) fc(); + }); + } + + public onResetEnv(fc: any){ + // Create the button + const button = this._createButton(this.buttonsContainer, "reset_env"); + button.addEventListener("click", () => { + this.level.reset(); + if (fc) fc(); + }); + } + + public onResetAgent(fc: any){ + // Create the button + const button = this._createButton(this.buttonsContainer, "reset_agent"); + button.addEventListener("click", () => { + if (fc) fc(); + }); + } + + public onCustomEvent(name: string, fc: any){ + // Create the button + const button = this._createButton(this.buttonsContainer, name); + button.addEventListener("click", () => { + if (fc) fc(); + }); + } + + public onLoad(fc: any, options:eventLoadOptions = Object()){ + options.local = options.local || false; + // Create the button + const button = this._createButton(this.buttonsContainer, "load_trained_agent"); + // Create the fake input input file + var input_file = document.createElement('input'); // create new textarea + input_file.type = "file"; + input_file.accept = "*/*"; + input_file.style.display = "none"; + input_file.classList.add("metacar_button_input_file"); + input_file.id = "metacar_"+ this.canvasId + "_button_input_file"; + this.buttonsContainer.appendChild(input_file); + + input_file.addEventListener("change", (dump) => { + U.readDump(dump, (content: any) => { + if (fc) fc(content); + }); + }); + + button.addEventListener("click", () => { + if (options.local) { + input_file.click(); + } + else{ + if (fc) fc(); + } + }); + } +} \ No newline at end of file diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..e93b3a0 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,18 @@ +{ + "mode": "modules", + "out": "docs", + "src": "src/index.ts", + "theme": "default", + "ignoreCompilerErrors": "true", + "experimentalDecorators": "true", + "emitDecoratorMetadata": "true", + "target": "ES5", + "moduleResolution": "node", + "preserveConstEnums": "true", + "stripInternal": "true", + "suppressExcessPropertyErrors": "true", + "suppressImplicitAnyIndexErrors": "true", + "module": "commonjs", + "hideGenerator": true, + "excludePrivate": true +} \ No newline at end of file