added Animation

master
Ugo Finnendahl 5 years ago
parent cfe6fe5734
commit 8bd9c482ef
  1. 13
      index.html
  2. 6
      js/controls.js
  3. 30
      js/rl.js
  4. 200
      js/view.js

@ -11,10 +11,19 @@
<div id="canvas"></div> <div id="canvas"></div>
<nav> <nav>
<button class="button" onclick="machine.run(100)">run 100 episodes!</button> <button class="button" onclick="machine.run(100)">run 100 episodes!</button>
<button class="button" onclick="machine.auto_step();draw_map(map, machine.state);">auto step!</button> <button class="button" onclick="machine.auto_step();update_agent(machine.state, true);">auto step!</button>
<button class="button" onclick="machine.greedy_step();draw_map(map, machine.state);">greedy step!</button> <button class="button" onclick="machine.greedy_step();update_agent(machine.state, true);">greedy step!</button>
</nav> </nav>
</div> </div>
<script>
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]
];
</script>
<script src="js/rl.js"></script> <script src="js/rl.js"></script>
<script src="js/view.js"></script> <script src="js/view.js"></script>
<script src="js/controls.js"></script> <script src="js/controls.js"></script>

6
js/controls.js vendored

@ -27,10 +27,12 @@ function key_callback(e) {
tmp = dir_to_action(dir.DOWN); tmp = dir_to_action(dir.DOWN);
break; break;
} }
var ret = 1;
if (tmp != undefined){ 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); document.addEventListener('keydown', key_callback);

@ -60,33 +60,13 @@ class RL_machine {
run(episodes, max_steps_per_episode=10000){ run(episodes, max_steps_per_episode=10000){
for (var i = 0; i < episodes; i++) { for (var i = 0; i < episodes; i++) {
for (var j = 0; j < max_steps_per_episode; j++) { for (var j = 0; j < max_steps_per_episode; j++) {
if (this.auto_step() == 2) { if (this.auto_step() != 1) {
break; break;
} }
} }
this.new_episode(); 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) { 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}; const reward = {[tile.regular]:-1,[tile.dangerous]:-1000,[tile.end]:1000,[tile.start]:-1};
var maze = new Maze(map, reward); var maze = new Maze(map, reward);

@ -25,6 +25,8 @@ var grid_group = new Konva.Group();
var tile_group = new Konva.Group(); var tile_group = new Konva.Group();
var agent_group = new Konva.Group(); var agent_group = new Konva.Group();
var padding;
map_layer.add(map_group); map_layer.add(map_group);
stage.add(map_layer); stage.add(map_layer);
@ -37,10 +39,142 @@ function init_stage() {
map_group.setY(stage.height() / 2 - (stage.height() * 0.45)); map_group.setY(stage.height() / 2 - (stage.height() * 0.45));
} }
function draw_map(map, state) { function draw_agent(state) {
// cleanup // cleanup
agent_group.remove(); agent_group.remove();
agent_group = new Konva.Group(); 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.remove();
grid_group = new Konva.Group(); grid_group = new Konva.Group();
tile_group.remove(); tile_group.remove();
@ -48,9 +182,8 @@ function draw_map(map, state) {
init_stage(); init_stage();
map = map; padding = Math.min(map_group.height() / map.length, map_group.width() / map[0].length);
var padding = Math.min(map_group.height() / map.length, map_group.width() / map[0].length); var strokeW = padding/50;
var strokeW = 16 / Math.max(map.length, map[0].length);
const offset = strokeW / 2; const offset = strokeW / 2;
// x // x
for (let i = 0; i < map[0].length + 1; i++) { 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) { } else if (map[idy][idx] == tile.start) {
tile_group.add(new Konva.Rect({ tile_group.add(new Konva.Rect({
...layout, ...layout,
fill: '#ffc908', fill: '#ff0008',
opacity: 0.5, opacity: 0.5,
})) }))
} }
@ -108,63 +241,18 @@ function draw_map(map, state) {
y: -(map_group.height() - grid_group.height()) / 2, 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(grid_group);
map_group.add(tile_group); map_group.add(tile_group);
map_group.add(agent_group);
map_layer.draw(); map_layer.draw();
} }
draw_map(map, 32);
window.addEventListener('resize', function() { window.addEventListener('resize', function() {
canvas_width = canvas.offsetWidth; canvas_width = canvas.offsetWidth;
canvas_height = canvas.offsetHeight; canvas_height = canvas.offsetHeight;
draw_map(map, machine.state); draw_map(map);
draw_agent(machine.state);
}); });
draw_map(map)
draw_agent(machine.state)

Loading…
Cancel
Save