master
Ugo Finnendahl 5 years ago
parent cfc52a33a2
commit c5d0176dc0
  1. 13
      css/src/style.scss
  2. 2
      css/style.min.css
  3. 20
      index.html
  4. 10
      js/rl.js
  5. 120
      js/view.js

@ -17,3 +17,16 @@ nav{
top: 10px; top: 10px;
left: 10px; left: 10px;
} }
.absolute{
position: absolute;
top:0;
left:0;
}
.plot{
position: absolute;
top: 2vh;
right: 2vw;
width: 20vw;
height: 10vw;
}

2
css/style.min.css vendored

@ -1 +1 @@
*{margin:0;padding:0}#container{height:100vh;position:relative}#canvas{height:100%}nav{position:absolute;top:10px;left:10px} *{margin:0;padding:0}#container{height:100vh;position:relative}#canvas{height:100%}nav{position:absolute;top:10px;left:10px}.absolute{position:absolute;top:0;left:0}.plot{position:absolute;top:2vh;right:2vw;width:20vw;height:10vw}

@ -6,23 +6,21 @@
<script src='https://unpkg.com/vue/dist/vue.js'></script> <script src='https://unpkg.com/vue/dist/vue.js'></script>
<script src='https://cdn.jsdelivr.net/npm/vue-konva@2.0.11/umd/vue-konva.min.js'></script> <script src='https://cdn.jsdelivr.net/npm/vue-konva@2.0.11/umd/vue-konva.min.js'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<script src="https://unpkg.com/vue-chartjs@3.4.2/dist/vue-chartjs.js"></script>
<title>RL exhibit - prototype</title> <title>RL exhibit - prototype</title>
<link rel="stylesheet" href="css/style.min.css"> <link rel="stylesheet" href="css/style.min.css">
</head> </head>
<body> <body>
<div id="app" style="position:absolute; top:0px;"> <div id="app">
<v-stage ref="stage" :config="stage_config"> <v-stage ref="stage" :config="stage_config">
<v-layer ref="local_layer" :config="local_layer"> <!-- <v-layer ref="local_layer" :config="local_layer">
<v-group ref="map_group" :config="map_config"> <v-group ref="map_group" :config="map_config">
<!-- <v-group ref="grid_group">
<v-line v-for="y in maze.height+1" :config="get_grid_line_config(y-1, true)"></v-line>
<v-line v-for="x in maze.width+1" :config="get_grid_line_config(x-1)"></v-line>
</v-group> -->
<v-rect v-for="(t_type, idx) in maze.map.flat()" :config="get_tile_config(idx, t_type, true)" :key="idx"></v-rect> <v-rect v-for="(t_type, idx) in maze.map.flat()" :config="get_tile_config(idx, t_type, true)" :key="idx"></v-rect>
<v-regular-polygon :config="get_agent_config()"></v-regular-polygon> <v-regular-polygon :config="agent_config"></v-regular-polygon>
</v-group> </v-group>
</v-layer> </v-layer> -->
<v-layer ref="map_layer"> <v-layer ref="map_layer">
<v-group ref="mini_map_group" :config="mini_map_config"> <v-group ref="mini_map_group" :config="mini_map_config">
<!-- <v-group ref="grid_group"> <!-- <v-group ref="grid_group">
@ -30,13 +28,14 @@
<v-line v-for="x in maze.width+1" :config="get_grid_line_config(x-1)"></v-line> <v-line v-for="x in maze.width+1" :config="get_grid_line_config(x-1)"></v-line>
</v-group> --> </v-group> -->
<v-rect v-for="(t_type, idx) in maze.map.flat()" :config="get_tile_config(idx, t_type)" :key="idx"></v-rect> <v-rect v-for="(t_type, idx) in maze.map.flat()" :config="get_tile_config(idx, t_type)" :key="idx"></v-rect>
<v-regular-polygon :config="get_agent_config()"></v-regular-polygon> <v-regular-polygon :config="agent_config"></v-regular-polygon>
</v-group> </v-group>
</v-layer> </v-layer>
</v-stage> </v-stage>
<line-chart css-classes="plot" :chart-data="datacollection" :options="{responsive: true, maintainAspectRatio: false,scales: {xAxes: [{ticks:{maxTicksLimit:11}}]},legend: {display: false}}"></line-chart>
</div> </div>
<nav> <nav>
<button class="button" onclick="machine.run(100)">run 100 episodes!</button> <button class="button" onclick="machine.run(1)">run 100 episodes!</button>
<button class="button" onclick="machine.auto_step();">auto step!</button> <button class="button" onclick="machine.auto_step();">auto step!</button>
<button class="button" onclick="machine.greedy_step();">greedy step!</button> <button class="button" onclick="machine.greedy_step();">greedy step!</button>
</nav> </nav>
@ -52,6 +51,5 @@
<script src="js/rl.js"></script> <script src="js/rl.js"></script>
<script src="js/controls.js"></script> <script src="js/controls.js"></script>
<script src="js/view.js"></script> <script src="js/view.js"></script>
</body> </body>
</html> </html>

@ -21,6 +21,7 @@ class RL_machine {
this.epsilon = epsilon; this.epsilon = epsilon;
this.score = 0; this.score = 0;
this.running = false; this.running = false;
this.score_history = [];
} }
reset_machine(){ reset_machine(){
this.q_table = this.q_table.map((c) => c.map((a) => a.fill(0))); this.q_table = this.q_table.map((c) => c.map((a) => a.fill(0)));
@ -31,6 +32,7 @@ class RL_machine {
// add_new_episode_callback // add_new_episode_callback
this.episode++; this.episode++;
this.state = this.start_state; this.state = this.start_state;
this.score_history.push(this.score);
this.score = 0; this.score = 0;
} }
auto_step(){ auto_step(){
@ -66,7 +68,7 @@ class RL_machine {
break; break;
} }
} }
this.new_episode(); // this.new_episode();
} }
this.running = false; this.running = false;
} }
@ -170,7 +172,7 @@ class Maze {
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);
var learning_rate = 1; var learning_rate = 0.75;
var discount_factor = 1; var discount_factor = 0.8;
var machine = new RL_machine(maze.actions, maze.transactions, maze.rewards, maze.start_state, maze.end_states, -999, learning_rate, discount_factor, 0.5); var machine = new RL_machine(maze.actions, maze.transactions, maze.rewards, maze.start_state, maze.end_states, -999, learning_rate, discount_factor, 0.2);

@ -1,3 +1,58 @@
Vue.component('line-chart', {
extends: VueChartJs.Line,
mixins: [VueChartJs.mixins.reactiveProp],
props: ['options'],
// mixins: [VueChartJs.mixins.reactiveData],
// props: ['options','labels', 'datasets'],
// watch: {
// 'labels': function(new_val) {
// this.chartData = {
// 'labels': new_val,
// 'datasets': this.datasets};
// },
// 'datasets': {
// deep:true,
// handler: function(new_val) {
// this.chartData = {
// 'labels': this.labels,
// 'datasets': new_val};
// }
// }
// },
mounted () {
this.renderChart(this.chartData, this.options);
},
})
Array.prototype.simpleSMA=function(N) {
return this.map(
function(el,index, _arr) {
return _arr.filter(
function(x2,i2) {
return i2 <= index && i2 > index - N;
})
.reduce(
function(last, current,index, arr){
return (current/arr.length + last);
},0);
});
};
Array.prototype.max=function() {
return this.map(
function(el,index, _arr) {
return _arr.filter(
function(x2,i2) {
return i2 <= index;
})
.reduce(
function(last, current){
return last > current ? last:current;
},-1000000000);
});
};
app = new Vue({ app = new Vue({
el: '#app', el: '#app',
data: { data: {
@ -7,6 +62,9 @@ app = new Vue({
maze: maze, maze: maze,
state: {x:0,y:0}, state: {x:0,y:0},
state_tween: new TimelineLite(), state_tween: new TimelineLite(),
score: machine.score,
score_history: machine.score_history,
labels: [],
}, },
created() { created() {
// Resize handler // Resize handler
@ -21,11 +79,52 @@ app = new Vue({
set: function(ne) { this._state=ne; $this.handleState(this._state); } set: function(ne) { this._state=ne; $this.handleState(this._state); }
}); });
machine.state = s; machine.state = s;
// Score wrapper
var s = machine.score;
var $this = this;
this.score = s;
Object.defineProperty(machine, 'score', {
get: function() { return this._score },
set: function(ne) { this._score=ne; $this.score=ne}
});
machine.score = s;
// Score history wrapper
var s = machine.score_history;
var $this = this;
this.score_history = s;
Object.defineProperty(machine, 'score_history', {
get: function() { return this._score_history },
set: function(ne) { this._score_history=ne; $this.score_history=ne}
});
machine.score_history = s;
}, },
destroyed() { destroyed() {
window.removeEventListener('resize', this.handleResize) window.removeEventListener('resize', this.handleResize)
}, },
computed: { computed: {
datacollection: function () {
return {
labels: Array.from(Array(this.score_history.length).keys()),
datasets: [
{
label: 'Data One',
backgroundColor: 'rgb(0,0,0,0)',
data: this.score_history.simpleSMA(Math.round(50)),
fill: false,
borderColor: 'rgb(255, 159, 64)',
pointRadius: 1,
},
{
label: 'Data One',
backgroundColor: 'rgb(0,0,0,0)',
data: this.score_history.max(),
fill: false,
borderColor: 'rgb(64, 159, 255)',
pointRadius: 1,
},
]
}
},
stage_config: function () { stage_config: function () {
return { return {
width: this.width, width: this.width,
@ -34,13 +133,11 @@ app = new Vue({
}, },
mini_map_config: function () { mini_map_config: function () {
return { return {
// x: this.stage_config.width * 0.5 - (Math.round(maze.width * this.base_size)/2), x:this.width/2-(this.base_size*(this.maze.width)/2),
// y: this.stage_config.height * 0.5 - (Math.round(maze.height * this.base_size)/2), y:this.height/2-(this.base_size*(this.maze.height)/2),
x:this.width-(Math.round(maze.width * this.base_size)*0.2)-30,
y:30,
scale:{ scale:{
x: 0.2, x: 1,
y: 0.2 y: 1
} }
} }
}, },
@ -75,10 +172,12 @@ app = new Vue({
x: -this.base_size / 2, x: -this.base_size / 2,
y: -this.base_size / 2 y: -this.base_size / 2
}, },
x: this.base_size*this.state.x,
y: this.base_size*this.state.y,
} }
}, },
base_size: function () { base_size: function () {
return Math.min(this.stage_config.height * 0.9 / this.maze.height, this.stage_config.width * 0.6 / this.maze.width); return Math.min(this.stage_config.height * 0.8 / this.maze.height, this.stage_config.width * 0.5 / this.maze.width);
}, },
strokeW: function () { strokeW: function () {
return this.base_size / 50; return this.base_size / 50;
@ -119,13 +218,6 @@ app = new Vue({
strokeWidth: this.strokeW, strokeWidth: this.strokeW,
} }
}, },
get_agent_config: function () {
return{
...this.agent_config,
x: this.base_size*this.state.x,
y: this.base_size*this.state.y,
}
},
get_tile_type: function (state){ get_tile_type: function (state){
var pos = this.s2p(state); var pos = this.s2p(state);
if (pos.y > maze.height){ if (pos.y > maze.height){

Loading…
Cancel
Save