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); }, }) var palette = ['#00429d', '#06449d', '#0d469d', '#12489d', '#164a9d', '#1a4c9c', '#1d4e9c', '#20509c', '#23529c', '#26549c', '#28569b', '#2b589b', '#2d5a9b', '#305c9b', '#325e9a', '#34609a', '#37629a', '#39649a', '#3b6699', '#3d6899', '#3f6999', '#416b98', '#436d98', '#456f97', '#477197', '#497397', '#4b7596', '#4d7796', '#4f7995', '#517b95', '#537d94', '#557f94', '#578193', '#598392', '#5b8592', '#5d8791', '#5f8991', '#618a90', '#638c8f', '#658e8f', '#67908e', '#69928d', '#6b948d', '#6d968c', '#6f988b', '#719a8b', '#739c8a', '#759e89', '#77a088', '#79a287', '#7ba386', '#7da586', '#80a785', '#82a984', '#84ab83', '#86ad82', '#88af81', '#8ab180', '#8cb37f', '#8fb57e', '#91b77d', '#93b87c', '#95ba7a', '#97bc79', '#9abe78', '#9cc077', '#9ec276', '#a1c474', '#a3c673', '#a5c872', '#a8c970', '#aacb6f', '#accd6e', '#afcf6c', '#b1d16b', '#b4d369', '#b6d568', '#b8d766', '#bbd864', '#bdda63', '#c0dc61', '#c2de5f', '#c5e05d', '#c8e25b', '#cae459', '#cde657', '#cfe755', '#d2e953', '#d5eb50', '#d7ed4e', '#daef4b', '#ddf049', '#dff246', '#e2f443', '#e5f640', '#e8f83c', '#ebfa39', '#edfb35', '#f0fd31', '#f3ff2c'] 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({ el: '#app', components: { VueSlider: window['vue-slider-component'] }, data: { width: 0, height: 0, q_table: machine.q_table, maze: maze, state: { x: 0, y: 0 }, state_tween: new TimelineLite(), score: machine.score, score_history: machine.score_history, labels: [], learning_rate: machine.lr, discount_factor: machine.df, epsilon: machine.epsilon, slider_config: { min: 0, max: 1, duration: 0, interval: 0.01, tooltip: 'none' } }, created() { // Resize handler window.addEventListener('resize', this.handleResize) this.handleResize(); // State wrapper var s = machine.state; var $this = this; this.state = this.s2p(s); Object.defineProperty(machine, 'state', { get: function() { return this._state }, set: function(ne) { this._state = ne; $this.handleState(this._state); } }); 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() { window.removeEventListener('resize', this.handleResize) }, 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, // }, ] } }, plot_options: function() { var $this = this; return { responsive: true, maintainAspectRatio: false, scales: { xAxes: [{ // type: 'linear', ticks: { maxTicksLimit: 8, maxRotation: 0, } }] }, legend: { display: false } } }, stage_config: function() { return { width: this.width, height: this.height, } }, mini_map_config: function() { return { x: this.width / 2 - (this.base_size * (this.maze.width) / 2), y: this.height / 2 - (this.base_size * (this.maze.height) / 2), scale: { x: 1, y: 1 } } }, local_layer: function() { return { x: this.width / 2, y: this.height / 2, scale: { x: 2, y: 2 } } }, map_config: function() { return { x: this.base_size * (this.maze.width - this.state.x), y: this.base_size * (this.maze.height - this.state.y), offset: { x: this.base_size * this.maze.width + this.base_size / 2, y: this.base_size * this.maze.height + this.base_size / 2, } } }, agent_config: function() { return { sides: 5, radius: this.base_size / 3, fill: '#00D2FF', stroke: 'black', strokeWidth: this.strokeW, offset: { x: -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() { return Math.min(this.stage_config.height * 0.8 / this.maze.height, this.stage_config.width * 0.5 / this.maze.width); }, strokeW: function() { return this.base_size / 50; }, extreme_q_values: function(){ var max = -10*30; var min = 10*30; for (field in this.q_table) { for (key in this.q_table[field]){ if (this.q_table[field][key]max){ max = this.q_table[field][key]; } } } return {min:min,max:max}; } }, methods: { s2p: function(state) { return { x: (state % this.maze.width), y: Math.floor(state / this.maze.width), } }, p2s: function(x, y) { return x + y * this.maze.width; }, handleResize: function() { this.width = window.innerWidth; this.height = window.innerHeight; }, handleState: function(s) { if (!machine.running) { this.state_tween.to(this.state, 0.2, { x: this.s2p(s).x, y: this.s2p(s).y }); } else { this.state = this.s2p(s); } // this.hidden_state = s; }, get_grid_line_config: function(idx, y = false) { var offset = this.strokeW / 2; if (y) { var points = [-offset, Math.round(idx * this.base_size), this.base_size * this.maze.width + offset, Math.round(idx * this.base_size)]; } else { var points = [Math.round(idx * this.base_size), -offset, Math.round(idx * this.base_size), this.base_size * this.maze.height + offset]; } return { points: points, stroke: '#ddd', strokeWidth: this.strokeW, } }, get_tile_type: function(state) { var pos = this.s2p(state); if (pos.y > maze.height) { return null; } else if (pos.x > maze.width) { return null; } else { return maze.map[pos.y][pos.x]; } }, in_plus: function(pos1, pos2) { if (Math.abs(pos1.x - pos2.x) + Math.abs(pos1.y - pos2.y) < 2) { return true; } return false; }, get_field_config: function(state) { var pos = this.s2p(state); return { x: this.base_size * pos.x+this.base_size/2, y: this.base_size * pos.y+this.base_size/2, } }, get_q_text_config: function (val, i) { var off, key; switch (i) { case 1: off = { align: "center", verticalAlign: "top", }; key = dir.UP; break; case 2: off = { align: "right", verticalAlign: "middle", }; key = dir.RIGHT; break; case 3: off = { align: "center", verticalAlign: "bottom", }; key = dir.DOWN; break; case 4: off = { align: "left", verticalAlign: "middle", }; key = dir.LEFT; break; } if (val[key] === undefined) { return {} } return { fontSize: this.base_size/7, fontFamily: 'Calibri', fill: 'black', text: +val[key].toFixed(2)+'', width: this.base_size-5, height: this.base_size-5, ...off, offset: { x: (this.base_size-5)/2, y: (this.base_size-5)/2, } } }, get_triangle_config: function(value, d) { var rot = 0; switch (d) { case dir.UP: rot = -90; break; case dir.RIGHT: rot = 0; break; case dir.DOWN: rot = 90; break; case dir.LEFT: rot = 180; break; } var $this = this; var norma_value = (value-this.extreme_q_values.min)/((this.extreme_q_values.max-this.extreme_q_values.min)||1); return { sceneFunc: function(context, shape) { context.beginPath(); context.moveTo(0, 0); context.lineTo($this.base_size / 2, $this.base_size / 2); context.lineTo($this.base_size / 2, -$this.base_size / 2); context.lineTo(0, 0); context.closePath(); // (!) Konva specific method, it is very important context.fillStrokeShape(shape); }, fill: palette[Math.round(norma_value*99)], stroke: 'black', strokeWidth: 0, rotation: rot, } }, get_tile_config: function(i, t_type, local = false) { var pos = this.s2p(i); var over = {}; // not in plus if (local) { if (!this.in_plus(this.s2p(i), { x: Math.round(this.state.x), y: Math.round(this.state.y) })) { over = { opacity: 0, fill: "#eee" }; } else if (i != this.p2s(Math.round(this.state.x), Math.round(this.state.y))) { over = { opacity: 1, fill: "#eee" }; } } const layout = { x: this.base_size * pos.x, y: this.base_size * pos.y, width: this.base_size, height: this.base_size, stroke: '#ddd', strokeWidth: this.strokeW, }; switch (t_type) { case tile.regular: return { ...layout, fill: '#fff', opacity: 1, ...over, } case tile.end: return { ...layout, fill: '#0eb500', opacity: 1, ...over, } case tile.start: return { ...layout, fill: '#ff0008', opacity: 1, ...over, } case tile.dangerous: return { ...layout, fill: '#FF7B17', opacity: 1, ...over, } case tile.wall: return { ...layout, fill: '#000000', opacity: 1, ...over, } } } }, watch: { learning_rate: function(new_val) { machine.lr = new_val; render_latex(); }, discount_factor: function(new_val) { machine.df = new_val; render_latex(); }, epsilon: function(new_val) { machine.epsilon = new_val; } } }) function render_latex() { // (1-lr) * Q[state, action] + lr * (reward + gamma * np.max(Q[new_state, :]) katex.render(`Q(s,a)\\leftarrow${(1-machine.lr).toFixed(2)}Q(s,a)+${machine.lr.toFixed(2)}(reward + ${machine.df.toFixed(2)} * \\max_a(Q(s', a))`, document.getElementById('test'),{displayMode: true,}); } render_latex();