From 8bd9c482efab09352915029590c8736f16a5d0f0 Mon Sep 17 00:00:00 2001 From: Ugo Date: Sat, 5 Oct 2019 15:12:09 +0200 Subject: [PATCH] added Animation --- index.html | 13 +++- js/controls.js | 6 +- js/rl.js | 30 +------- js/view.js | 200 +++++++++++++++++++++++++++++++++++-------------- 4 files changed, 160 insertions(+), 89 deletions(-) diff --git a/index.html b/index.html index 379de73..29024ee 100644 --- a/index.html +++ b/index.html @@ -11,10 +11,19 @@
+ diff --git a/js/controls.js b/js/controls.js index 26b0f56..0d57b76 100644 --- a/js/controls.js +++ b/js/controls.js @@ -27,10 +27,12 @@ function key_callback(e) { tmp = dir_to_action(dir.DOWN); break; } + var ret = 1; if (tmp != undefined){ - machine.step(tmp) + agent.do_action(tmp, true); + ret = machine.step(tmp); } - draw_map(map, machine.state); + update_agent(machine.state, true); } document.addEventListener('keydown', key_callback); diff --git a/js/rl.js b/js/rl.js index c6e2698..5b276f9 100644 --- a/js/rl.js +++ b/js/rl.js @@ -60,33 +60,13 @@ class RL_machine { run(episodes, max_steps_per_episode=10000){ for (var i = 0; i < episodes; i++) { for (var j = 0; j < max_steps_per_episode; j++) { - if (this.auto_step() == 2) { + if (this.auto_step() != 1) { break; } } this.new_episode(); } } - current_solution(max_steps_per_episode=10000){ - let temp_state = this.start_state; - let score = 0; - let states = [temp_state]; - let actions = []; - let scores = []; - for (var j = 0; j < max_steps_per_episode; j++) { - let ac = argMax(this.q_table[temp_state]); - temp_state = this.transactions(temp_state, ac); - let sc = this.rewards[temp_state]; - score += sc; - actions.push(ac); - scores.push(sc); - states.push(temp_state); - if (this.end_states.indexOf(temp_state) >= 0 || score < this.end_score){ - return {actions: actions, scores: scores, states: states} - } - } - return {actions: actions, scores: scores, states: states} - } } function argMax(array) { @@ -203,14 +183,6 @@ class Maze { } } -var map = [ - [0, 0, 4, 2, 0, 0, 0, 0], - [0, 0, 4, 4, 4, 4, 0, 0], - [4, 0, 0, 0, 0, 4, 0, 4], - [0, 0, 4, 0, 0, 0, 0, 0], - [1, 0, 4, 0, 4, 0, 0, 4] -]; - const reward = {[tile.regular]:-1,[tile.dangerous]:-1000,[tile.end]:1000,[tile.start]:-1}; var maze = new Maze(map, reward); diff --git a/js/view.js b/js/view.js index 1b692d0..edbb3cc 100644 --- a/js/view.js +++ b/js/view.js @@ -25,6 +25,8 @@ var grid_group = new Konva.Group(); var tile_group = new Konva.Group(); var agent_group = new Konva.Group(); +var padding; + map_layer.add(map_group); stage.add(map_layer); @@ -37,10 +39,142 @@ function init_stage() { map_group.setY(stage.height() / 2 - (stage.height() * 0.45)); } -function draw_map(map, state) { +function draw_agent(state) { // cleanup agent_group.remove(); agent_group = new Konva.Group(); + + agent = { + "konva": new Konva.RegularPolygon({ + offset: { + x: -padding / 2, + y: -padding / 2 + }, + sides: 5, + radius: padding / 3, + fill: '#00D2FF', + stroke: 'black', + strokeWidth: padding / 50 + }), + "target_x": undefined, + "target_y": undefined, + "todos": [], + "_padding": padding, + set padding(pad){ + this._padding = pad; + this.konva.offset({x: -padding / 2, y: -padding / 2}); + this.konva.radius(padding / 3); + this.konva.strokeWidth(padding / 50); + }, + get x() { + return Math.floor(this.konva.getX() / this._padding); + }, + set x(pos) { + // this.target_x = sort([0, pos, map[0].length - 1])[1] * this._padding; + this.konva.setX(sort([0, pos, map[0].length - 1])[1] * this._padding); + map_layer.draw(); + }, + get y() { + return Math.floor(this.konva.getY() / this._padding); + }, + set y(pos) { + // this.target_y = sort([0, pos, map.length - 1])[1] * this._padding; + this.konva.setY(sort([0, pos, map.length - 1])[1] * this._padding); + map_layer.draw(); + }, + do_action(action, animate=false){ + let d = maze.get_direction(machine.state, action); + var y = this.y; + var x = this.x; + var fun; + switch (d) { + case dir.UP: + y--; + fun = function () {return [this.x,this.y-1]}.bind(this); + break; + case dir.RIGHT: + x++; + fun = function () {return [this.x+1,this.y]}.bind(this); + break; + case dir.DOWN: + y++; + fun = function () {return [this.x,this.y+1]}.bind(this); + break; + case dir.LEFT: + x--; + fun = function () {return [this.x-1,this.y]}.bind(this); + break; + } + if (animate){ + console.log(fun); + this.todos.push(fun); + console.log(this.todos); + } else { + this.todos = []; + this.target_x = undefined; + this.target_y = undefined; + this.x = x; + this.y = y; + } + }, + set_state(state, animate=false){ + let y = Math.floor(state/map[0].length); + let x = state%map[0].length; + if (animate){ + this.todos.push([x,y]) + } else { + this.todos = []; + this.target_x = undefined; + this.target_y = undefined; + this.x = x; + this.y = y; + } + }, + "animation": function () { + return new Konva.Animation(function(frame) { + if (this.target_x === undefined || this.target_y === undefined) { + if (this.todos.length == 0){ + return + } + var current = this.todos.shift(); + if (current.length == 2){ + [this.target_x,this.target_y] = current; + } else { + [this.target_x,this.target_y] = current(); + } + } + var x = sort([0, this.target_x, map[1].length - 1])[1] * this._padding; + var y = sort([0, this.target_y, map.length - 1])[1] * this._padding; + var vec = [x-this.konva.getX(),y-this.konva.getY()]; + const length = Math.sqrt(vec[0]**2+vec[1]**2); + var step = frame.timeDiff*(padding/100); + if (length < step){ + this.konva.setX(x); + this.konva.setY(y); + this.target_x = undefined; + this.target_y = undefined; + } else { + vec[0] = vec[0]/length; + vec[1] = vec[1]/length; + this.konva.setX(this.konva.getX()+vec[0]*step); + this.konva.setY(this.konva.getY()+vec[1]*step); + } + }.bind(this), map_layer); + } + } + var anim = agent.animation(); + agent.set_state(state); + anim.start(); + agent_group.add(agent.konva); + map_group.add(agent_group); +} + +function update_agent(state, animate=false) { + agent.padding = padding; + agent.set_state(state, animate); +} + +function draw_map(map) { grid_group.remove(); grid_group = new Konva.Group(); tile_group.remove(); @@ -48,9 +182,8 @@ function draw_map(map, state) { init_stage(); - map = map; - var padding = Math.min(map_group.height() / map.length, map_group.width() / map[0].length); - var strokeW = 16 / Math.max(map.length, map[0].length); + padding = Math.min(map_group.height() / map.length, map_group.width() / map[0].length); + var strokeW = padding/50; const offset = strokeW / 2; // x for (let i = 0; i < map[0].length + 1; i++) { @@ -96,7 +229,7 @@ function draw_map(map, state) { } else if (map[idy][idx] == tile.start) { tile_group.add(new Konva.Rect({ ...layout, - fill: '#ffc908', + fill: '#ff0008', opacity: 0.5, })) } @@ -108,63 +241,18 @@ function draw_map(map, state) { y: -(map_group.height() - grid_group.height()) / 2, }); - agent = { - "konva": new Konva.RegularPolygon({ - offset: { - x: -padding / 2, - y: -padding / 2 - }, - sides: 5, - radius: padding / 3, - fill: '#00D2FF', - stroke: 'black', - strokeWidth: 2 - }), - get x() { - return Math.floor(this.konva.getX() / padding); - }, - set x(pos) { - this.konva.setX(sort([0, pos, map[0].length - 1])[1] * padding); - map_layer.draw(); - }, - get y() { - return Math.floor(this.konva.getY() / padding); - }, - set y(pos) { - this.konva.setY(sort([0, pos, map.length - 1])[1] * padding); - map_layer.draw(); - }, - up() { - this.y--; - }, - down() { - this.y++; - }, - left() { - this.x++; - }, - right() { - this.x--; - }, - set_state(state){ - this.y = Math.floor(state/map[0].length); - this.x = state%map[0].length; - } - } - - agent.set_state(state); - agent_group.add(agent.konva); - map_group.add(grid_group); map_group.add(tile_group); - map_group.add(agent_group); + map_layer.draw(); } -draw_map(map, 32); - window.addEventListener('resize', function() { canvas_width = canvas.offsetWidth; canvas_height = canvas.offsetHeight; - draw_map(map, machine.state); + draw_map(map); + draw_agent(machine.state); }); + +draw_map(map) +draw_agent(machine.state)