diff --git a/demo/dist/metacar.min.js b/demo/dist/metacar.min.js
index c8a11f2..d3df358 100644
--- a/demo/dist/metacar.min.js
+++ b/demo/dist/metacar.min.js
@@ -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\nvar embeddedUrl = {\n fullCity: \"embedded://level/fullCity\"\n};\nvar embeddedContent = {\n level: {\n fullCity: _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__[\"fullCity\"]\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\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?");
/***/ }),
@@ -171,6 +171,18 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ }),
+/***/ "./src/embedded/level/level_1.ts":
+/*!***************************************!*\
+ !*** ./src/embedded/level/level_1.ts ***!
+ \***************************************/
+/*! exports provided: level1 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"level1\", function() { return level1; });\nvar level1 = {\n \"cars\": [\n {\n \"mx\": 5,\n \"my\": 3,\n \"line\": 0,\n \"auto\": true\n },\n {\n \"mx\": 5,\n \"my\": 3,\n \"line\": 1,\n \"auto\": true\n },\n {\n \"mx\": 5,\n \"my\": 1,\n \"line\": 0,\n \"auto\": true\n }\n ],\n \"tree\": [\n {\n \"x\": 120,\n \"y\": 50\n },\n {\n \"x\": 86.5,\n \"y\": 85\n },\n {\n \"x\": 94.5,\n \"y\": 135\n },\n {\n \"x\": 30.5,\n \"y\": 128\n },\n {\n \"x\": 72.5,\n \"y\": 190\n },\n {\n \"x\": 23.5,\n \"y\": 232\n },\n {\n \"x\": 110.5,\n \"y\": 242\n },\n {\n \"x\": 43.5,\n \"y\": 33\n },\n {\n \"x\": 18.5,\n \"y\": 178\n },\n {\n \"x\": 305.5,\n \"y\": 125\n },\n {\n \"x\": 360.5,\n \"y\": 122\n }\n ],\n \"asset\": [\n {\n \"x\": 252.5,\n \"y\": 142\n },\n {\n \"x\": 86.5,\n \"y\": 85\n },\n {\n \"x\": 94.5,\n \"y\": 135\n },\n {\n \"x\": 30.5,\n \"y\": 128\n },\n {\n \"x\": 252.5,\n \"y\": 26\n },\n {\n \"x\": 195.5,\n \"y\": 0\n },\n {\n \"x\": 307.5,\n \"y\": 1\n },\n {\n \"x\": 72.5,\n \"y\": 190\n },\n {\n \"x\": 23.5,\n \"y\": 232\n },\n {\n \"x\": 110.5,\n \"y\": 242\n },\n {\n \"x\": 43.5,\n \"y\": 33\n },\n {\n \"x\": 35.5,\n \"y\": 185\n },\n {\n \"x\": 18.5,\n \"y\": 178\n },\n {\n \"x\": 289.5,\n \"y\": 126\n },\n {\n \"x\": 337.5,\n \"y\": 126\n },\n {\n \"x\": 305.5,\n \"y\": 125\n },\n {\n \"x\": 360.5,\n \"y\": 122\n },\n {\n \"x\": 492.5,\n \"y\": 56\n },\n {\n \"x\": 503.5,\n \"y\": 136\n },\n {\n \"x\": 504.5,\n \"y\": 58\n }\n ],\n \"bench\": [\n {\n \"x\": 252.5,\n \"y\": 142\n },\n {\n \"x\": 252.5,\n \"y\": 26\n }\n ],\n \"road\": [\n {\n \"x\": 240,\n \"y\": 60\n },\n {\n \"x\": 240,\n \"y\": 60\n },\n {\n \"x\": 480,\n \"y\": 0\n },\n {\n \"x\": 480,\n \"y\": 60\n },\n {\n \"x\": 480,\n \"y\": 120\n },\n {\n \"x\": 480,\n \"y\": 180\n },\n {\n \"x\": 480,\n \"y\": 240\n },\n {\n \"x\": 300,\n \"y\": 180\n },\n {\n \"x\": 300,\n \"y\": 180\n },\n {\n \"x\": 360,\n \"y\": 180\n },\n {\n \"x\": 420,\n \"y\": 180\n },\n {\n \"x\": 420,\n \"y\": 120\n },\n {\n \"x\": 420,\n \"y\": 60\n },\n {\n \"x\": 360,\n \"y\": 60\n },\n {\n \"x\": 300,\n \"y\": 60\n },\n {\n \"x\": 420,\n \"y\": 120\n }\n ],\n \"house3\": [\n {\n \"x\": 503.5,\n \"y\": 136\n }\n ],\n \"house\": [\n {\n \"x\": 504.5,\n \"y\": 58\n }\n ],\n \"map\": [\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n \"↱\",\n \"↠\",\n \"↔\",\n \"↔\",\n \"↰\",\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n \"↕\",\n 0,\n 0,\n 0,\n \"↟\",\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n \"↳\",\n \"↔\",\n \"↔\",\n \"↔\",\n \"↲\",\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ]\n ],\n \"agent\": {\n \"mx\": 4,\n \"my\": 3,\n \"line\": 0,\n \"motion\": {\n \"type\": \"BasicMotionEngine\",\n \"options\": {\n \"rotationStep\": 0.5,\n \"actions\": [\n \"UP\",\n \"LEFT\",\n \"RIGHT\"\n ]\n }\n }\n }\n};\n\n\n//# sourceURL=webpack://metacar/./src/embedded/level/level_1.ts?");
+
+/***/ }),
+
/***/ "./src/global.ts":
/*!***********************!*\
!*** ./src/global.ts ***!
@@ -203,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.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.render = function () {\n this.app.ticker.start();\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.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?");
/***/ }),
@@ -215,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 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 }\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.render = function () {\n this.level.render();\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 _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?");
/***/ }),
@@ -235,11 +247,11 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/*!**********************!*\
!*** ./src/utils.ts ***!
\**********************/
-/*! exports provided: loadJSON, loadEmbeddedURL, loadLocalStorageURL, loadCustomURL, mod, argMax, mean, saveAs, shuffleArray, keyboard */
+/*! exports provided: loadJSON, loadEmbeddedURL, loadLocalStorageURL, loadCustomURL, mod, argMax, mean, saveAs, readDump, shuffleArray, keyboard */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadJSON\", function() { return loadJSON; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadEmbeddedURL\", function() { return loadEmbeddedURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadLocalStorageURL\", function() { return loadLocalStorageURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadCustomURL\", function() { return loadCustomURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mod\", function() { return mod; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"argMax\", function() { return argMax; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return mean; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saveAs\", function() { return saveAs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"shuffleArray\", function() { return shuffleArray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"keyboard\", function() { return keyboard; });\n/* harmony import */ var _embedded__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./embedded */ \"./src/embedded.ts\");\n\nfunction loadJSON(url, callback) {\n var xobj = new XMLHttpRequest();\n xobj.overrideMimeType(\"application/json\");\n xobj.open('GET', url, true);\n xobj.onreadystatechange = function () {\n if (xobj.readyState == 4 && xobj.status == 200) {\n callback(xobj.responseText);\n }\n };\n xobj.send(null);\n}\nfunction loadEmbeddedURL(url, callback) {\n var split = url.split(\"//\");\n split = split[1].split(\"/\");\n callback(_embedded__WEBPACK_IMPORTED_MODULE_0__[\"embeddedContent\"][split[0]][split[1]]);\n}\nfunction loadLocalStorageURL(url, callback) {\n}\nfunction loadCustomURL(url, callback) {\n var customCall = {\n \"embedded:\": loadEmbeddedURL,\n \"http://\": loadJSON,\n \"https://\": loadJSON,\n \"localstorage://\": loadLocalStorageURL\n };\n var split = url.split(\"//\");\n customCall[split[0]](url, callback);\n}\nfunction mod(n, m) {\n return ((n % m) + m) % m;\n}\nfunction argMax(array) {\n if (array.every(function (v) { return v == -Infinity; })) {\n return Math.floor(Math.random() * array.length);\n }\n return array.map(function (x, i) { return [x, i]; }).reduce(function (r, a) { return (a[0] > r[0] ? a : r); })[1];\n}\nfunction mean(array) {\n if (array.length == 0)\n return null;\n var sum = array.reduce(function (a, b) { return a + b; });\n var avg = sum / array.length;\n return avg;\n}\nfunction saveAs(content, name) {\n var a = document.createElement(\"a\");\n document.body.appendChild(a);\n var blob = new Blob([content], { type: 'application/octet-binary' }), tmpURL = window.URL.createObjectURL(blob);\n a.href = tmpURL;\n a.download = name;\n a.click();\n window.URL.revokeObjectURL(tmpURL);\n a.href = \"\";\n}\nfunction shuffleArray(array) {\n for (var i = array.length - 1; i > 0; i--) {\n var j = Math.floor(Math.random() * (i + 1));\n var temp = array[i];\n array[i] = array[j];\n array[j] = temp;\n }\n return array;\n}\nfunction keyboard(keyCode) {\n var key = {};\n key.code = keyCode;\n key.isDown = false;\n key.isUp = true;\n key.press = undefined;\n key.release = undefined;\n key.downHandler = function (event) {\n if (event.keyCode === key.code) {\n if (key.isUp && key.press)\n key.press();\n key.isDown = true;\n key.isUp = false;\n }\n event.preventDefault();\n };\n key.upHandler = function (event) {\n if (event.keyCode === key.code) {\n if (key.isDown && key.release)\n key.release();\n key.isDown = false;\n key.isUp = true;\n }\n event.preventDefault();\n };\n window.addEventListener(\"keydown\", key.downHandler.bind(key), false);\n window.addEventListener(\"keyup\", key.upHandler.bind(key), false);\n return key;\n}\n\n\n//# sourceURL=webpack://metacar/./src/utils.ts?");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadJSON\", function() { return loadJSON; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadEmbeddedURL\", function() { return loadEmbeddedURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadLocalStorageURL\", function() { return loadLocalStorageURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadCustomURL\", function() { return loadCustomURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mod\", function() { return mod; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"argMax\", function() { return argMax; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return mean; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saveAs\", function() { return saveAs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"readDump\", function() { return readDump; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"shuffleArray\", function() { return shuffleArray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"keyboard\", function() { return keyboard; });\n/* harmony import */ var _embedded__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./embedded */ \"./src/embedded.ts\");\n\nfunction loadJSON(url, callback) {\n var xobj = new XMLHttpRequest();\n xobj.overrideMimeType(\"application/json\");\n xobj.open('GET', url, true);\n xobj.onreadystatechange = function () {\n if (xobj.readyState == 4 && xobj.status == 200) {\n callback(xobj.responseText);\n }\n };\n xobj.send(null);\n}\nfunction loadEmbeddedURL(url, callback) {\n var split = url.split(\"//\");\n split = split[1].split(\"/\");\n callback(_embedded__WEBPACK_IMPORTED_MODULE_0__[\"embeddedContent\"][split[0]][split[1]]);\n}\nfunction loadLocalStorageURL(url, callback) {\n}\nfunction loadCustomURL(url, callback) {\n var customCall = {\n \"embedded:\": loadEmbeddedURL,\n \"http://\": loadJSON,\n \"https://\": loadJSON,\n \"localstorage://\": loadLocalStorageURL\n };\n var split = url.split(\"//\");\n customCall[split[0]](url, callback);\n}\nfunction mod(n, m) {\n return ((n % m) + m) % m;\n}\nfunction argMax(array) {\n if (array.every(function (v) { return v == -Infinity; })) {\n return Math.floor(Math.random() * array.length);\n }\n return array.map(function (x, i) { return [x, i]; }).reduce(function (r, a) { return (a[0] > r[0] ? a : r); })[1];\n}\nfunction mean(array) {\n if (array.length == 0)\n return null;\n var sum = array.reduce(function (a, b) { return a + b; });\n var avg = sum / array.length;\n return avg;\n}\nfunction saveAs(content, name) {\n var a = document.createElement(\"a\");\n document.body.appendChild(a);\n var blob = new Blob([content], { type: 'application/octet-binary' }), tmpURL = window.URL.createObjectURL(blob);\n a.href = tmpURL;\n a.download = name;\n a.click();\n window.URL.revokeObjectURL(tmpURL);\n a.href = \"\";\n}\nfunction readDump(dump, callback) {\n var input = dump.target;\n var file = input.files[0];\n var reader = new FileReader();\n reader.readAsText(file, \"UTF-8\");\n reader.onload = function (evt) {\n callback(evt.target.result);\n };\n}\nfunction shuffleArray(array) {\n for (var i = array.length - 1; i > 0; i--) {\n var j = Math.floor(Math.random() * (i + 1));\n var temp = array[i];\n array[i] = array[j];\n array[j] = temp;\n }\n return array;\n}\nfunction keyboard(keyCode) {\n var key = {};\n key.code = keyCode;\n key.isDown = false;\n key.isUp = true;\n key.press = undefined;\n key.release = undefined;\n key.downHandler = function (event) {\n if (event.keyCode === key.code) {\n if (key.isUp && key.press)\n key.press();\n key.isDown = true;\n key.isUp = false;\n }\n };\n key.upHandler = function (event) {\n if (event.keyCode === key.code) {\n if (key.isDown && key.release)\n key.release();\n key.isDown = false;\n key.isUp = true;\n }\n };\n window.addEventListener(\"keydown\", key.downHandler.bind(key), false);\n window.addEventListener(\"keyup\", key.upHandler.bind(key), false);\n return key;\n}\n\n\n//# sourceURL=webpack://metacar/./src/utils.ts?");
/***/ }),
diff --git a/demo/webapp/index.html b/demo/webapp/full_city.html
similarity index 100%
rename from demo/webapp/index.html
rename to demo/webapp/full_city.html
diff --git a/demo/webapp/level1.html b/demo/webapp/level1.html
new file mode 100644
index 0000000..8142c67
--- /dev/null
+++ b/demo/webapp/level1.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Hello World
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/webapp/public/js/level1.js b/demo/webapp/public/js/level1.js
new file mode 100644
index 0000000..83619b1
--- /dev/null
+++ b/demo/webapp/public/js/level1.js
@@ -0,0 +1,28 @@
+// Get the url of the desired level
+let levelUrl = metacar.level.level1;
+// Create the environement (canvasID, levelUrl)
+var env = new metacar.env("canvas", levelUrl);
+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("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);
+ });
+});
diff --git a/dist/metacar.min.js b/dist/metacar.min.js
index c8a11f2..d3df358 100644
--- a/dist/metacar.min.js
+++ b/dist/metacar.min.js
@@ -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\nvar embeddedUrl = {\n fullCity: \"embedded://level/fullCity\"\n};\nvar embeddedContent = {\n level: {\n fullCity: _embedded_level_full_city__WEBPACK_IMPORTED_MODULE_0__[\"fullCity\"]\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\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?");
/***/ }),
@@ -171,6 +171,18 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ }),
+/***/ "./src/embedded/level/level_1.ts":
+/*!***************************************!*\
+ !*** ./src/embedded/level/level_1.ts ***!
+ \***************************************/
+/*! exports provided: level1 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"level1\", function() { return level1; });\nvar level1 = {\n \"cars\": [\n {\n \"mx\": 5,\n \"my\": 3,\n \"line\": 0,\n \"auto\": true\n },\n {\n \"mx\": 5,\n \"my\": 3,\n \"line\": 1,\n \"auto\": true\n },\n {\n \"mx\": 5,\n \"my\": 1,\n \"line\": 0,\n \"auto\": true\n }\n ],\n \"tree\": [\n {\n \"x\": 120,\n \"y\": 50\n },\n {\n \"x\": 86.5,\n \"y\": 85\n },\n {\n \"x\": 94.5,\n \"y\": 135\n },\n {\n \"x\": 30.5,\n \"y\": 128\n },\n {\n \"x\": 72.5,\n \"y\": 190\n },\n {\n \"x\": 23.5,\n \"y\": 232\n },\n {\n \"x\": 110.5,\n \"y\": 242\n },\n {\n \"x\": 43.5,\n \"y\": 33\n },\n {\n \"x\": 18.5,\n \"y\": 178\n },\n {\n \"x\": 305.5,\n \"y\": 125\n },\n {\n \"x\": 360.5,\n \"y\": 122\n }\n ],\n \"asset\": [\n {\n \"x\": 252.5,\n \"y\": 142\n },\n {\n \"x\": 86.5,\n \"y\": 85\n },\n {\n \"x\": 94.5,\n \"y\": 135\n },\n {\n \"x\": 30.5,\n \"y\": 128\n },\n {\n \"x\": 252.5,\n \"y\": 26\n },\n {\n \"x\": 195.5,\n \"y\": 0\n },\n {\n \"x\": 307.5,\n \"y\": 1\n },\n {\n \"x\": 72.5,\n \"y\": 190\n },\n {\n \"x\": 23.5,\n \"y\": 232\n },\n {\n \"x\": 110.5,\n \"y\": 242\n },\n {\n \"x\": 43.5,\n \"y\": 33\n },\n {\n \"x\": 35.5,\n \"y\": 185\n },\n {\n \"x\": 18.5,\n \"y\": 178\n },\n {\n \"x\": 289.5,\n \"y\": 126\n },\n {\n \"x\": 337.5,\n \"y\": 126\n },\n {\n \"x\": 305.5,\n \"y\": 125\n },\n {\n \"x\": 360.5,\n \"y\": 122\n },\n {\n \"x\": 492.5,\n \"y\": 56\n },\n {\n \"x\": 503.5,\n \"y\": 136\n },\n {\n \"x\": 504.5,\n \"y\": 58\n }\n ],\n \"bench\": [\n {\n \"x\": 252.5,\n \"y\": 142\n },\n {\n \"x\": 252.5,\n \"y\": 26\n }\n ],\n \"road\": [\n {\n \"x\": 240,\n \"y\": 60\n },\n {\n \"x\": 240,\n \"y\": 60\n },\n {\n \"x\": 480,\n \"y\": 0\n },\n {\n \"x\": 480,\n \"y\": 60\n },\n {\n \"x\": 480,\n \"y\": 120\n },\n {\n \"x\": 480,\n \"y\": 180\n },\n {\n \"x\": 480,\n \"y\": 240\n },\n {\n \"x\": 300,\n \"y\": 180\n },\n {\n \"x\": 300,\n \"y\": 180\n },\n {\n \"x\": 360,\n \"y\": 180\n },\n {\n \"x\": 420,\n \"y\": 180\n },\n {\n \"x\": 420,\n \"y\": 120\n },\n {\n \"x\": 420,\n \"y\": 60\n },\n {\n \"x\": 360,\n \"y\": 60\n },\n {\n \"x\": 300,\n \"y\": 60\n },\n {\n \"x\": 420,\n \"y\": 120\n }\n ],\n \"house3\": [\n {\n \"x\": 503.5,\n \"y\": 136\n }\n ],\n \"house\": [\n {\n \"x\": 504.5,\n \"y\": 58\n }\n ],\n \"map\": [\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n \"↱\",\n \"↠\",\n \"↔\",\n \"↔\",\n \"↰\",\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n \"↕\",\n 0,\n 0,\n 0,\n \"↟\",\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n \"↳\",\n \"↔\",\n \"↔\",\n \"↔\",\n \"↲\",\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ]\n ],\n \"agent\": {\n \"mx\": 4,\n \"my\": 3,\n \"line\": 0,\n \"motion\": {\n \"type\": \"BasicMotionEngine\",\n \"options\": {\n \"rotationStep\": 0.5,\n \"actions\": [\n \"UP\",\n \"LEFT\",\n \"RIGHT\"\n ]\n }\n }\n }\n};\n\n\n//# sourceURL=webpack://metacar/./src/embedded/level/level_1.ts?");
+
+/***/ }),
+
/***/ "./src/global.ts":
/*!***********************!*\
!*** ./src/global.ts ***!
@@ -203,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.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.render = function () {\n this.app.ticker.start();\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.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?");
/***/ }),
@@ -215,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 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 }\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.render = function () {\n this.level.render();\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 _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?");
/***/ }),
@@ -235,11 +247,11 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/*!**********************!*\
!*** ./src/utils.ts ***!
\**********************/
-/*! exports provided: loadJSON, loadEmbeddedURL, loadLocalStorageURL, loadCustomURL, mod, argMax, mean, saveAs, shuffleArray, keyboard */
+/*! exports provided: loadJSON, loadEmbeddedURL, loadLocalStorageURL, loadCustomURL, mod, argMax, mean, saveAs, readDump, shuffleArray, keyboard */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadJSON\", function() { return loadJSON; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadEmbeddedURL\", function() { return loadEmbeddedURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadLocalStorageURL\", function() { return loadLocalStorageURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadCustomURL\", function() { return loadCustomURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mod\", function() { return mod; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"argMax\", function() { return argMax; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return mean; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saveAs\", function() { return saveAs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"shuffleArray\", function() { return shuffleArray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"keyboard\", function() { return keyboard; });\n/* harmony import */ var _embedded__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./embedded */ \"./src/embedded.ts\");\n\nfunction loadJSON(url, callback) {\n var xobj = new XMLHttpRequest();\n xobj.overrideMimeType(\"application/json\");\n xobj.open('GET', url, true);\n xobj.onreadystatechange = function () {\n if (xobj.readyState == 4 && xobj.status == 200) {\n callback(xobj.responseText);\n }\n };\n xobj.send(null);\n}\nfunction loadEmbeddedURL(url, callback) {\n var split = url.split(\"//\");\n split = split[1].split(\"/\");\n callback(_embedded__WEBPACK_IMPORTED_MODULE_0__[\"embeddedContent\"][split[0]][split[1]]);\n}\nfunction loadLocalStorageURL(url, callback) {\n}\nfunction loadCustomURL(url, callback) {\n var customCall = {\n \"embedded:\": loadEmbeddedURL,\n \"http://\": loadJSON,\n \"https://\": loadJSON,\n \"localstorage://\": loadLocalStorageURL\n };\n var split = url.split(\"//\");\n customCall[split[0]](url, callback);\n}\nfunction mod(n, m) {\n return ((n % m) + m) % m;\n}\nfunction argMax(array) {\n if (array.every(function (v) { return v == -Infinity; })) {\n return Math.floor(Math.random() * array.length);\n }\n return array.map(function (x, i) { return [x, i]; }).reduce(function (r, a) { return (a[0] > r[0] ? a : r); })[1];\n}\nfunction mean(array) {\n if (array.length == 0)\n return null;\n var sum = array.reduce(function (a, b) { return a + b; });\n var avg = sum / array.length;\n return avg;\n}\nfunction saveAs(content, name) {\n var a = document.createElement(\"a\");\n document.body.appendChild(a);\n var blob = new Blob([content], { type: 'application/octet-binary' }), tmpURL = window.URL.createObjectURL(blob);\n a.href = tmpURL;\n a.download = name;\n a.click();\n window.URL.revokeObjectURL(tmpURL);\n a.href = \"\";\n}\nfunction shuffleArray(array) {\n for (var i = array.length - 1; i > 0; i--) {\n var j = Math.floor(Math.random() * (i + 1));\n var temp = array[i];\n array[i] = array[j];\n array[j] = temp;\n }\n return array;\n}\nfunction keyboard(keyCode) {\n var key = {};\n key.code = keyCode;\n key.isDown = false;\n key.isUp = true;\n key.press = undefined;\n key.release = undefined;\n key.downHandler = function (event) {\n if (event.keyCode === key.code) {\n if (key.isUp && key.press)\n key.press();\n key.isDown = true;\n key.isUp = false;\n }\n event.preventDefault();\n };\n key.upHandler = function (event) {\n if (event.keyCode === key.code) {\n if (key.isDown && key.release)\n key.release();\n key.isDown = false;\n key.isUp = true;\n }\n event.preventDefault();\n };\n window.addEventListener(\"keydown\", key.downHandler.bind(key), false);\n window.addEventListener(\"keyup\", key.upHandler.bind(key), false);\n return key;\n}\n\n\n//# sourceURL=webpack://metacar/./src/utils.ts?");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadJSON\", function() { return loadJSON; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadEmbeddedURL\", function() { return loadEmbeddedURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadLocalStorageURL\", function() { return loadLocalStorageURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"loadCustomURL\", function() { return loadCustomURL; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mod\", function() { return mod; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"argMax\", function() { return argMax; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return mean; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saveAs\", function() { return saveAs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"readDump\", function() { return readDump; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"shuffleArray\", function() { return shuffleArray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"keyboard\", function() { return keyboard; });\n/* harmony import */ var _embedded__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./embedded */ \"./src/embedded.ts\");\n\nfunction loadJSON(url, callback) {\n var xobj = new XMLHttpRequest();\n xobj.overrideMimeType(\"application/json\");\n xobj.open('GET', url, true);\n xobj.onreadystatechange = function () {\n if (xobj.readyState == 4 && xobj.status == 200) {\n callback(xobj.responseText);\n }\n };\n xobj.send(null);\n}\nfunction loadEmbeddedURL(url, callback) {\n var split = url.split(\"//\");\n split = split[1].split(\"/\");\n callback(_embedded__WEBPACK_IMPORTED_MODULE_0__[\"embeddedContent\"][split[0]][split[1]]);\n}\nfunction loadLocalStorageURL(url, callback) {\n}\nfunction loadCustomURL(url, callback) {\n var customCall = {\n \"embedded:\": loadEmbeddedURL,\n \"http://\": loadJSON,\n \"https://\": loadJSON,\n \"localstorage://\": loadLocalStorageURL\n };\n var split = url.split(\"//\");\n customCall[split[0]](url, callback);\n}\nfunction mod(n, m) {\n return ((n % m) + m) % m;\n}\nfunction argMax(array) {\n if (array.every(function (v) { return v == -Infinity; })) {\n return Math.floor(Math.random() * array.length);\n }\n return array.map(function (x, i) { return [x, i]; }).reduce(function (r, a) { return (a[0] > r[0] ? a : r); })[1];\n}\nfunction mean(array) {\n if (array.length == 0)\n return null;\n var sum = array.reduce(function (a, b) { return a + b; });\n var avg = sum / array.length;\n return avg;\n}\nfunction saveAs(content, name) {\n var a = document.createElement(\"a\");\n document.body.appendChild(a);\n var blob = new Blob([content], { type: 'application/octet-binary' }), tmpURL = window.URL.createObjectURL(blob);\n a.href = tmpURL;\n a.download = name;\n a.click();\n window.URL.revokeObjectURL(tmpURL);\n a.href = \"\";\n}\nfunction readDump(dump, callback) {\n var input = dump.target;\n var file = input.files[0];\n var reader = new FileReader();\n reader.readAsText(file, \"UTF-8\");\n reader.onload = function (evt) {\n callback(evt.target.result);\n };\n}\nfunction shuffleArray(array) {\n for (var i = array.length - 1; i > 0; i--) {\n var j = Math.floor(Math.random() * (i + 1));\n var temp = array[i];\n array[i] = array[j];\n array[j] = temp;\n }\n return array;\n}\nfunction keyboard(keyCode) {\n var key = {};\n key.code = keyCode;\n key.isDown = false;\n key.isUp = true;\n key.press = undefined;\n key.release = undefined;\n key.downHandler = function (event) {\n if (event.keyCode === key.code) {\n if (key.isUp && key.press)\n key.press();\n key.isDown = true;\n key.isUp = false;\n }\n };\n key.upHandler = function (event) {\n if (event.keyCode === key.code) {\n if (key.isDown && key.release)\n key.release();\n key.isDown = false;\n key.isUp = true;\n }\n };\n window.addEventListener(\"keydown\", key.downHandler.bind(key), false);\n window.addEventListener(\"keyup\", key.upHandler.bind(key), false);\n return key;\n}\n\n\n//# sourceURL=webpack://metacar/./src/utils.ts?");
/***/ }),
diff --git a/src/basic_motion_engine.ts b/src/basic_motion_engine.ts
index e3875e6..fa337d0 100644
--- a/src/basic_motion_engine.ts
+++ b/src/basic_motion_engine.ts
@@ -77,15 +77,15 @@ export class BasicMotionEngine extends MotionEngine {
}
}
- turnLeft(){
+ turnLeft():void {
/*
Turn left
*/
- this.car.rotation -= this.rotationStep*Math.PI;
- this.lidar.rotation = this.car.rotation;
+ this.car.rotation -= this.rotationStep*Math.PI
+ this.lidar.rotation = this.car.rotation;
}
- turnRight(){
+ turnRight():void {
/*
Turn right
*/
@@ -93,14 +93,14 @@ export class BasicMotionEngine extends MotionEngine {
this.lidar.rotation = this.car.rotation;
}
- moveForward(){
+ moveForward():void {
/*
Move forward
*/
this.car.v = 1;
}
- moveBackward(){
+ moveBackward():void {
/*
Move backward
*/
diff --git a/src/embedded.ts b/src/embedded.ts
index 2426558..04da286 100644
--- a/src/embedded.ts
+++ b/src/embedded.ts
@@ -1,11 +1,14 @@
import {fullCity} from "./embedded/level/full_city";
+import {level1} from "./embedded/level/level_1";
export const embeddedUrl: any = {
- fullCity: "embedded://level/fullCity"
+ fullCity: "embedded://level/fullCity",
+ level1: "embedded://level/level1"
}
export const embeddedContent: any = {
level: {
- fullCity: fullCity
+ fullCity: fullCity,
+ level1: level1
}
}
\ No newline at end of file
diff --git a/src/embedded/level/level_1.ts b/src/embedded/level/level_1.ts
new file mode 100644
index 0000000..fa44b7a
--- /dev/null
+++ b/src/embedded/level/level_1.ts
@@ -0,0 +1,316 @@
+export const level1: any = {
+ "cars": [
+ {
+ "mx": 5,
+ "my": 3,
+ "line": 0,
+ "auto": true
+ },
+ {
+ "mx": 5,
+ "my": 3,
+ "line": 1,
+ "auto": true
+ },
+ {
+ "mx": 5,
+ "my": 1,
+ "line": 0,
+ "auto": true
+ }
+ ],
+ "tree": [
+ {
+ "x": 120,
+ "y": 50
+ },
+ {
+ "x": 86.5,
+ "y": 85
+ },
+ {
+ "x": 94.5,
+ "y": 135
+ },
+ {
+ "x": 30.5,
+ "y": 128
+ },
+ {
+ "x": 72.5,
+ "y": 190
+ },
+ {
+ "x": 23.5,
+ "y": 232
+ },
+ {
+ "x": 110.5,
+ "y": 242
+ },
+ {
+ "x": 43.5,
+ "y": 33
+ },
+ {
+ "x": 18.5,
+ "y": 178
+ },
+ {
+ "x": 305.5,
+ "y": 125
+ },
+ {
+ "x": 360.5,
+ "y": 122
+ }
+ ],
+ "asset": [
+ {
+ "x": 252.5,
+ "y": 142
+ },
+ {
+ "x": 86.5,
+ "y": 85
+ },
+ {
+ "x": 94.5,
+ "y": 135
+ },
+ {
+ "x": 30.5,
+ "y": 128
+ },
+ {
+ "x": 252.5,
+ "y": 26
+ },
+ {
+ "x": 195.5,
+ "y": 0
+ },
+ {
+ "x": 307.5,
+ "y": 1
+ },
+ {
+ "x": 72.5,
+ "y": 190
+ },
+ {
+ "x": 23.5,
+ "y": 232
+ },
+ {
+ "x": 110.5,
+ "y": 242
+ },
+ {
+ "x": 43.5,
+ "y": 33
+ },
+ {
+ "x": 35.5,
+ "y": 185
+ },
+ {
+ "x": 18.5,
+ "y": 178
+ },
+ {
+ "x": 289.5,
+ "y": 126
+ },
+ {
+ "x": 337.5,
+ "y": 126
+ },
+ {
+ "x": 305.5,
+ "y": 125
+ },
+ {
+ "x": 360.5,
+ "y": 122
+ },
+ {
+ "x": 492.5,
+ "y": 56
+ },
+ {
+ "x": 503.5,
+ "y": 136
+ },
+ {
+ "x": 504.5,
+ "y": 58
+ }
+ ],
+ "bench": [
+ {
+ "x": 252.5,
+ "y": 142
+ },
+ {
+ "x": 252.5,
+ "y": 26
+ }
+ ],
+ "road": [
+ {
+ "x": 240,
+ "y": 60
+ },
+ {
+ "x": 240,
+ "y": 60
+ },
+ {
+ "x": 480,
+ "y": 0
+ },
+ {
+ "x": 480,
+ "y": 60
+ },
+ {
+ "x": 480,
+ "y": 120
+ },
+ {
+ "x": 480,
+ "y": 180
+ },
+ {
+ "x": 480,
+ "y": 240
+ },
+ {
+ "x": 300,
+ "y": 180
+ },
+ {
+ "x": 300,
+ "y": 180
+ },
+ {
+ "x": 360,
+ "y": 180
+ },
+ {
+ "x": 420,
+ "y": 180
+ },
+ {
+ "x": 420,
+ "y": 120
+ },
+ {
+ "x": 420,
+ "y": 60
+ },
+ {
+ "x": 360,
+ "y": 60
+ },
+ {
+ "x": 300,
+ "y": 60
+ },
+ {
+ "x": 420,
+ "y": 120
+ }
+ ],
+ "house3": [
+ {
+ "x": 503.5,
+ "y": 136
+ }
+ ],
+ "house": [
+ {
+ "x": 504.5,
+ "y": 58
+ }
+ ],
+ "map": [
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ "↱",
+ "↠",
+ "↔",
+ "↔",
+ "↰",
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ "↕",
+ 0,
+ 0,
+ 0,
+ "↟",
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ "↳",
+ "↔",
+ "↔",
+ "↔",
+ "↲",
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ ],
+ "agent": {
+ "mx": 4,
+ "my": 3,
+ "line": 0,
+ "motion": {
+ "type": "BasicMotionEngine",
+ "options": {
+ "rotationStep": 0.5,
+ "actions": [
+ "UP",
+ "LEFT",
+ "RIGHT"
+ ]
+ }
+ }
+ }
+}
diff --git a/src/level.ts b/src/level.ts
index 8d48093..2f6654a 100644
--- a/src/level.ts
+++ b/src/level.ts
@@ -60,6 +60,15 @@ export class Level {
});
}
+ render(val: boolean){
+ if (val){
+ this.app.ticker.start();
+ }
+ else{
+ this.app.ticker.stop();
+ }
+ }
+
createLevel(info: LevelInfo){
/*
Create the level
@@ -98,14 +107,6 @@ export class Level {
this.app.ticker.add((delta: number) => this.loop(delta));
}
- render(){
- /*
- If the rendering has been stopped, this Method
- restart the rendering.
- */
- this.app.ticker.start();
- }
-
reset(){
/*
Reset the game.
diff --git a/src/metacar.ts b/src/metacar.ts
index 88fd91f..60e7cec 100644
--- a/src/metacar.ts
+++ b/src/metacar.ts
@@ -5,6 +5,10 @@
import {Level, LevelInfo} from "./level";
import * as U from "./utils";
+export interface eventLoadOptions {
+ computer: boolean;
+}
+
export class MetaCar {
private isPlaying: boolean;
@@ -12,6 +16,9 @@ export class MetaCar {
private level: Level;
private canvasId: string;
private levelUrl: string;
+ private eventList: string[] = ["train", "play", "stop", "reset_env", "reset_agent", "save", "load"]
+ private eventCallback: any[];
+ private buttonsContainer: HTMLDivElement;
constructor(canvasId: string, levelUrl: string) {
/**
@@ -27,6 +34,23 @@ export class MetaCar {
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;
}
load(level: string, agent: any): Promise{
@@ -56,8 +80,8 @@ export class MetaCar {
});
document.getElementById("stop").addEventListener("click", () => {
this.isPlaying = false;
- this.agent.stop();
this.level.render();
+ this.agent.stop();
});
document.getElementById("reset").addEventListener("click", () => {
this.level.reset();
@@ -74,12 +98,117 @@ export class MetaCar {
*/
}
- render(){
+ 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();
+ });
+ }
+
+ 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);
+ }
+ const event = this.eventList[index];
+ if (event != "load"){
+ this.eventCallback[index](fc);
+ }
+ else{
+ this.eventCallback[index](fc, options);
+ }
+ }
+
+ render(val: boolean){
/*
- Render the environement
- again
+ Choose whether to render the environement.
*/
- this.level.render();
+ this.level.render(val);
}
save(content: any, file_name: string){
diff --git a/src/utils.ts b/src/utils.ts
index 6a995ef..d7e0334 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -122,18 +122,16 @@ function readBuffer(buffer) {
}
*/
-/*
-function readDump(e, callback) {
- var input = e.target;
+export function readDump(dump: any, callback: any) {
+ var input = dump.target;
var file = input.files[0];
-
var reader = new FileReader();
reader.readAsText(file, "UTF-8");
- reader.onload = (evt) => {
+ reader.onload = (evt:any) => {
callback(evt.target.result);
}
}
-*/
+
export function shuffleArray(array: number[]) {
/**
@@ -174,7 +172,7 @@ export function keyboard(keyCode: any ) {
key.isDown = true;
key.isUp = false;
}
- event.preventDefault();
+ //event.preventDefault();
};
key.upHandler = (event: any) => {
@@ -183,9 +181,9 @@ export function keyboard(keyCode: any ) {
key.isDown = false;
key.isUp = true;
}
- event.preventDefault();
+ //event.preventDefault();
};
-
+
//Attach event listeners
window.addEventListener(
"keydown", key.downHandler.bind(key), false