├── genomes ├── .gitkeep └── ninja.json ├── assets └── top_score.png ├── .gitignore ├── package.json ├── LICENSE ├── index.js ├── Scanner.js ├── README.md ├── UI.js ├── Learner.js └── GameManipulator.js /genomes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/top_score.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanseidel/IAMDinosaur/HEAD/assets/top_score.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Dependency directory 6 | node_modules 7 | robotjs 8 | 9 | # Saved files 10 | genomes/* 11 | !genomes/.gitkeep 12 | !genomes/ninja.json -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iamdinosaur", 3 | "version": "1.0.0", 4 | "description": "A simple artificial inteligence to teach Google's Dinosaur to jump cactus", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Ivan Seidel", 10 | "license": "MIT", 11 | "dependencies": { 12 | "async": "^1.5.0", 13 | "blessed": "^0.1.81", 14 | "blessed-contrib": "^2.5.2", 15 | "lodash": "^3.10.1", 16 | "robotjs": "^0.4.5", 17 | "synaptic": "^1.0.5" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/ivanseidel/IAMDinosaur.git" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ivan Seidel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var robot = require('robotjs'); 2 | 3 | var GameManipulator = require('./GameManipulator'); 4 | var Learner = require('./Learner'); 5 | var Scanner = require('./Scanner'); 6 | var UI = require('./UI'); 7 | 8 | 9 | // Configure Robotjs 10 | robot.setMouseDelay(1); 11 | 12 | 13 | // Initialize Game 14 | GameManipulator.findGamePosition(); 15 | 16 | 17 | // Check for found game 18 | if (GameManipulator.offset) { 19 | // Uncomment this line to debug the 20 | // starting point of sensor (Check if it's detecting it correcly) 21 | 22 | // robot.moveMouse(GameManipulator.offset[0]+GameManipulator.sensors[0].offset[0], 23 | // GameManipulator.offset[1] + GameManipulator.sensors[0].offset[1]); 24 | 25 | robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]); 26 | } else { 27 | console.error('FAILED TO FIND GAME!'); 28 | process.exit(); 29 | } 30 | 31 | 32 | // Initialize UI 33 | UI.init(GameManipulator, Learner); 34 | 35 | 36 | // Init Learner 37 | Learner.init(GameManipulator, UI, 12, 4, 0.2); 38 | 39 | 40 | // Start reading game state and sensors 41 | setInterval(GameManipulator.readSensors, 40); 42 | setInterval(GameManipulator.readGameState, 200); 43 | 44 | 45 | // Start game (Example of API usage) 46 | /* 47 | function startGame () { 48 | var game = Math.round(Math.random() * 100); 49 | 50 | UI.logger.log('Queuing start... ', game); 51 | 52 | GameManipulator.startNewGame(function() { 53 | UI.logger.log('Game HAS started!', game); 54 | GameManipulator.onGameEnd = function () { 55 | UI.logger.log('Game HAS ended!', game); 56 | 57 | startGame(); 58 | } 59 | }); 60 | } 61 | */ -------------------------------------------------------------------------------- /Scanner.js: -------------------------------------------------------------------------------- 1 | var robot = require('robotjs'); 2 | 3 | // Cache screen size 4 | var screenSize = robot.getScreenSize(); 5 | 6 | // Indexes 7 | var X = 0; 8 | var Y = 1; 9 | 10 | 11 | // Create the "class" wrapper 12 | var Scanner = {}; 13 | 14 | 15 | // Check if the given position is outside the Screen 16 | Scanner.isOutOfBound = function (pos) { 17 | if ( pos[X] < 0 || pos[Y] < 0 || 18 | pos[X] >= screenSize.width || 19 | pos[Y] >= screenSize.height) { 20 | 21 | return true; 22 | } 23 | 24 | return false; 25 | } 26 | 27 | 28 | // Limits the x/y values of position to fit the screen 29 | Scanner.makeInBounds = function (pos) { 30 | 31 | if (pos[X] < 0) { 32 | pos[X] = 0; 33 | } 34 | 35 | if (pos[X] >= screenSize.width) { 36 | pos[X] = screenSize.width - 1; 37 | } 38 | 39 | if (pos[Y] < 0) { 40 | pos[Y] = 0; 41 | } 42 | 43 | if (pos[Y] >= screenSize.height) { 44 | pos[Y] = screenSize.height - 1; 45 | } 46 | 47 | return pos; 48 | } 49 | 50 | 51 | // Given start [X, Y], and a DELTA [dX, dY], 52 | // maps from "start", adding "delta" to position, 53 | // until "matchinColor" is found OR isOutOfBounds. 54 | // 55 | // If iterations reach > iterLimit: 56 | // returns null; 57 | // 58 | // if isOutOfBounds: 59 | // returns null 60 | // 61 | // otherwise: 62 | // return that point 63 | // 64 | // Example: (X direction) 65 | // scanUntil([0,0], [1, 0], "000000"); 66 | Scanner.scanUntil = function (start, delta, matchColor, inverted, iterLimit) { 67 | var color, current, iterations = 0; 68 | 69 | // (CLONE instead of using the real one) 70 | current = Scanner.makeInBounds([start[X], start[Y]]); 71 | 72 | if (delta[X] == 0 && delta[Y] == 0) { 73 | return null; 74 | } 75 | 76 | 77 | while (!Scanner.isOutOfBound(current)) { 78 | // Check current pixel 79 | color = robot.getPixelColor(current[X], current[Y]); 80 | 81 | if (!inverted && color.toString() == matchColor) { 82 | return current; 83 | } 84 | 85 | if (inverted && color.toString() != matchColor) { 86 | return current; 87 | } 88 | 89 | current[X] += delta[X]; 90 | current[Y] += delta[Y]; 91 | iterations++; 92 | 93 | if (iterations > iterLimit) { 94 | return null; 95 | } 96 | } 97 | 98 | return null; 99 | }; 100 | 101 | 102 | // Export the module 103 | module.exports = Scanner; 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IAMDinosaur 2 | 3 | ![IAMDinosaur](https://raw.githubusercontent.com/ivanseidel/IAMDinosaur/master/assets/top_score.png) 4 | 5 | A simple artificial intelligence to teach Google Chrome's offline dinosaur to 6 | jump cactus, using Neural Networks and a simple Genetic Algorithm. 7 | 8 | **Watch** this video to see it in action, and learn how it works: [Artificial Intelligence with Google's Dinosaur](https://youtu.be/P7XHzqZjXQs) 9 | 10 | ## Installation 11 | 12 | 1. Install `Node.js` on your computer. 13 | 14 | 2. Clone/download this folder to your computer. 15 | 16 | 3. run `npm install` within this folder 17 | 18 | 4. Open Chrome's dinosaur game and put aside the terminal (It MUST be on the same screen) 19 | **(Tip: go to developer tools, and under network, set to offline )** 20 | 21 | 5. run `node index` within this folder. If the game was located, it will move the cursor 22 | of the mouse to the origin of the `floor` of the dino. Press `s` key in the terminal to 23 | start learning. 24 | 25 | 26 | ## How does it work 27 | 28 | We have 3 different inputs read from the pixels of the screen: 29 | 30 | 1. Distance from the next cactus 31 | 2. Length of the next cactus 32 | 3. Speed of the current cactus 33 | 34 | We have also, one output with 3 possible states: 35 | 36 | 1. output < 0.45: Press DOWN key 37 | 2. output > 0.55: Press UP key 38 | 2. default: Release both keys 39 | 40 | ## Genetic Algorithm 41 | 42 | Each Generation consists of 12 neural networks (Genomes). 43 | 44 | Each genome is tested with the game, by constantly mapping the read 45 | inputs from the game to the inputs of the neural network, and by getting 46 | the output/activation from the network and applying to the keys of the 47 | keyboard. 48 | 49 | While testing each genome, we keep track of it's "fitness" by counting 50 | jumped cactus in the game. 51 | 52 | When an entire generation is completed, we remove the worst genomes until 53 | achieving `N` genomes. With those `N` genomes, we then select two randomly, 54 | and cross-over their values/configurations. After that, we apply random mutations 55 | in the values/configurations of the Neural Network, creating a new genome. 56 | 57 | We do the cross-over/mutation until we get 12 genomes again, and repeat it constantly. 58 | 59 | 60 | ## Implementation 61 | 62 | All the implementation was done using Node.js, with Synaptic (Neural Network library), 63 | and RobotJs (a library to read pixels and simulate key presses). 64 | 65 | There are a few files in the project: 66 | 67 | - `index.js`: It tight all things together. 68 | 69 | - `Scanner.js`: Basic abstraction layer above RobotJs library that reads the screen like 70 | ray tracing. Also have some utilities functions. 71 | 72 | - `UI.js`: Global scope for the UI management. It initializes and also updates the screen 73 | on changes. 74 | 75 | - `GameManipulator.js`: Has all the necessary code to read sensors, and apply outputs 76 | to the game. Is also responsible for computing points, getting the game state and 77 | triggering callbacks/listeners to real implementation. 78 | 79 | - `Learner.js`: It is the core implementation of the Genetic Algorithm. This is where 80 | "magic" happens, by running generations, doing "natural" selection, cross-over, mutation... 81 | 82 | 83 | ### How to: Load a genome 84 | 85 | 1. Make sure Genome is inside `genomes` folder with a `.json` extension 86 | 2. Run the program 87 | 3. Click the list in the terminal 88 | 4. Navigate up/down to the wanted file 89 | 5. Press `enter` (then, to start, press `s`) 90 | 91 | ### Some shortcuts 92 | 93 | 1. Run the program 94 | 2. Press `o` to save the generation 95 | 3. Press ´escape´, ´q´ or `C-c` to finish the process 96 | 97 | 98 | ### Be aware of a game bug 99 | 100 | The dino game has a anoying bug: It starts to "drift" to the right with time 101 | making the dino to be wrong offseted from the origin of the game. That, makes 102 | the program to read the dino as a cactus, since it is the same color. 103 | 104 | You can fix that by continuously refreshing the page, or, by pasting this code inside the 105 | console in the element inspector: 106 | 107 | ``` 108 | // Make sure the dino does not drift to the right 109 | setInterval(function (){Runner.instance_.tRex.xPos = 21}, 2000) 110 | ``` 111 | 112 | ## Development guidelines 113 | 114 | Please, follow the Node.js style guide from [Felix](https://github.com/felixge/node-style-guide). 115 | It is not complex, and has a great simple pattern for things. 116 | 117 | ## Credits 118 | 119 | - [Ivan Seidel](https://github.com/ivanseidel) 120 | - [João Pedro](https://github.com/joaopedrovbs) 121 | - [Tony Ngan](https://github.com/tngan) **The idea came from him** 122 | 123 | -------------------------------------------------------------------------------- /UI.js: -------------------------------------------------------------------------------- 1 | var contrib = require('blessed-contrib'); 2 | var blessed = require('blessed'); 3 | var fs = require('fs'); 4 | 5 | var screen = blessed.screen(); 6 | 7 | var UI = {}; 8 | var savegame = function(){ 9 | var jsonGenomes = []; 10 | for (var k in UI.learner.genomes) { 11 | jsonGenomes.push(UI.learner.genomes[k].toJSON()); 12 | } 13 | 14 | UI.logger.log('Saving '+jsonGenomes.length+' genomes...'); 15 | 16 | var dir = './genomes'; 17 | var fileName = dir + '/gen_'+UI.learner.generation+'_'+Date.now()+'.json'; 18 | fs.writeFile(fileName, JSON.stringify(jsonGenomes), function (err){ 19 | if (err) { 20 | UI.logger.log('Failed to save! '+err); 21 | } else { 22 | UI.logger.log('Saved to '+fileName); 23 | } 24 | 25 | UI.refreshFiles(); 26 | }); 27 | 28 | }; 29 | 30 | 31 | // Initialize UI objects 32 | UI.init = function (gameManipulator, learner) { 33 | UI.gm = gameManipulator; 34 | UI.learner = learner; 35 | 36 | UI.grid = new contrib.grid({ 37 | rows: 12, 38 | cols: 6, 39 | screen: screen 40 | }); 41 | 42 | 43 | // Build Sensor inputs 44 | UI.uiSensors = UI.grid.set(0, 0, 3, 6, contrib.bar, { 45 | label: 'Network Inputs', 46 | // bg: 'white', 47 | barWidth: 12, 48 | barSpacing: 1, 49 | xOffset: 0, 50 | maxHeight: 100, 51 | }); 52 | 53 | 54 | // Build Log box 55 | UI.logger = UI.grid.set(3, 0, 3, 6, contrib.log, { 56 | fg: 'green', 57 | selectedFg: 'green', 58 | label: 'Logs' 59 | }); 60 | 61 | 62 | // Current score/time view 63 | UI.uiScore = UI.grid.set(6, 0, 3, 3, blessed.Text, { 64 | label: 'Game Stats', 65 | // bg: 'green', 66 | fg: 'white', 67 | content: 'Loading...', 68 | align: 'center', 69 | }); 70 | 71 | 72 | // Current Genomes stats 73 | UI.uiGenomes = UI.grid.set(6, 3, 3, 3, blessed.Text, { 74 | label: 'Genome Stats', 75 | // bg: 'green', 76 | fg: 'white', 77 | content: 'Hey!', 78 | align: 'center', 79 | }); 80 | 81 | 82 | // Load Tree 83 | UI.savesTree = UI.grid.set(9, 0, 3, 3, contrib.tree, { 84 | label: 'Saved Genomes', 85 | }); 86 | 87 | 88 | // Callback for Loading genomes and focusing tree 89 | screen.key(['l','L'], UI.savesTree.focus.bind(UI.savesTree)); 90 | UI.savesTree.on('click', UI.savesTree.focus.bind(UI.savesTree)); 91 | UI.savesTree.on('select', function (item){ 92 | 93 | if (item.isFile) { 94 | var fileName = item.name; 95 | 96 | UI.logger.log('Loading genomes from file:'); 97 | UI.logger.log(fileName); 98 | 99 | var genomes = require('./genomes/'+fileName); 100 | 101 | UI.learner.loadGenomes(genomes); 102 | } else { 103 | UI.refreshFiles(); 104 | } 105 | }); 106 | 107 | UI.refreshFiles(); 108 | 109 | 110 | // Save Btn 111 | UI.btnSave = UI.grid.set(9, 3, 3, 3, blessed.box, { 112 | label: 'Save to File', 113 | bg: 'green', 114 | fg: 'red', 115 | content: '\n\n\n\nSave Genomes', 116 | align: 'center', 117 | }); 118 | 119 | UI.btnSave.on('click', function(){ 120 | savegame(); 121 | }); 122 | 123 | screen.key(['o','O'], function(){ 124 | savegame(); 125 | }); 126 | 127 | screen.key(['escape', 'q', 'C-c'], function(ch, key) { 128 | return process.exit(0); 129 | }); 130 | 131 | screen.key(['s'], function (ch, key){ 132 | if (learner.state === 'STOP') { 133 | learner.state = 'LEARNING'; 134 | gameManipulator.focusGame(); 135 | learner.startLearning(); 136 | } else { 137 | learner.state = 'STOP'; 138 | } 139 | }); 140 | 141 | screen.render(); 142 | }; 143 | 144 | 145 | // Read entire folder and select files that match a .json file 146 | UI.refreshFiles = function (){ 147 | var files = []; 148 | var fileData = { 149 | name: 'Saved Files', 150 | extended: true, 151 | children: [{ 152 | name: 'Refresh Folders' 153 | }] 154 | }; 155 | 156 | // Populate tree 157 | UI.logger.log('Reading genomes dir...'); 158 | var files = fs.readdirSync('./genomes'); 159 | for (var k in files) { 160 | if (files[k].indexOf('.json') >= 0) { 161 | 162 | fileData.children.push({ 163 | name: files[k], 164 | isFile: true, 165 | }); 166 | 167 | } 168 | } 169 | 170 | UI.savesTree.setData(fileData); 171 | } 172 | 173 | 174 | // Updates data on the screen and render it 175 | UI.render = function () { 176 | 177 | // Update data 178 | UI.uiSensors.setData({ 179 | titles: ['Distance', 'Size', 'Speed', 'Activation'], 180 | data: [ 181 | Math.round(UI.gm.sensors[0].value * 100), 182 | Math.round(UI.gm.sensors[0].size * 100), 183 | Math.round(UI.gm.sensors[0].speed * 100), 184 | Math.round(UI.gm.gameOutput * 100), 185 | ] 186 | }) 187 | 188 | // Set Genome stats and score 189 | var learn = UI.learner; 190 | var uiStats = ''; 191 | uiStats += 'Status: ' + learn.state + '\n'; 192 | uiStats += 'Fitness: ' + UI.gm.points + '\n'; 193 | uiStats += 'GameStatus: ' + UI.gm.gamestate + '\n'; 194 | uiStats += 'Generation: ' + learn.generation; 195 | uiStats += ' : ' + learn.genome + '/' + learn.genomes.length; 196 | UI.uiScore.setText(uiStats); 197 | 198 | if (UI.gm.gameOutput) { 199 | var str = ''; 200 | str += 'Action: ' + UI.gm.gameOutputString + '\n'; 201 | str += 'Activation: ' + UI.gm.gameOutput; 202 | UI.uiGenomes.setText(str); 203 | } else { 204 | UI.uiGenomes.setText('Loading...'); 205 | } 206 | 207 | // Render screen 208 | screen.render(); 209 | } 210 | 211 | // Continuously render screen 212 | setInterval(UI.render, 25); 213 | 214 | module.exports = UI; 215 | -------------------------------------------------------------------------------- /Learner.js: -------------------------------------------------------------------------------- 1 | var synaptic = require('synaptic'); 2 | var async = require('async'); 3 | var _ = require('lodash'); 4 | 5 | var Architect = synaptic.Architect; 6 | var Network = synaptic.Network; 7 | 8 | 9 | var Learn = { 10 | 11 | // Array of networks for current Genomes 12 | // (Genomes will be added the key `fitness`) 13 | genomes: [], 14 | 15 | // Current state of learning [STOP, LEARNING] 16 | state: 'STOP', 17 | 18 | // Current genome/generation tryout 19 | genome: 0, 20 | generation: 0, 21 | 22 | // Set this, to verify genome experience BEFORE running it 23 | shouldCheckExperience: false, 24 | 25 | }; 26 | 27 | 28 | // Initialize the Learner 29 | Learn.init = function (gameManip, ui, genomeUnits, selection, mutationProb) { 30 | Learn.gm = gameManip; 31 | Learn.ui = ui; 32 | 33 | Learn.genome = 0; 34 | Learn.generation = 0; 35 | 36 | Learn.genomeUnits = genomeUnits; 37 | Learn.selection = selection; 38 | Learn.mutationProb = mutationProb; 39 | } 40 | 41 | 42 | // Build genomes before calling executeGeneration. 43 | Learn.startLearning = function () { 44 | 45 | // Build genomes if needed 46 | while (Learn.genomes.length < Learn.genomeUnits) { 47 | Learn.genomes.push(Learn.buildGenome(3, 1)); 48 | } 49 | 50 | Learn.executeGeneration(); 51 | 52 | } 53 | 54 | 55 | // Given the entire generation of genomes (An array), 56 | // applyes method `executeGenome` for each element. 57 | // After all elements have completed executing: 58 | // 59 | // 1) Select best genomes 60 | // 2) Does cross over (except for 2 genomes) 61 | // 3) Does Mutation-only on remaining genomes 62 | // 4) Execute generation (recursivelly) 63 | Learn.executeGeneration = function (){ 64 | if (Learn.state == 'STOP') { 65 | return; 66 | } 67 | 68 | Learn.generation++; 69 | Learn.ui.logger.log('Executing generation '+Learn.generation); 70 | 71 | Learn.genome = 0; 72 | 73 | async.mapSeries(Learn.genomes, Learn.executeGenome, function (argument) { 74 | 75 | // Kill worst genomes 76 | Learn.genomes = Learn.selectBestGenomes(Learn.selection); 77 | 78 | // Copy best genomes 79 | var bestGenomes = _.clone(Learn.genomes); 80 | 81 | // Cross Over () 82 | while (Learn.genomes.length < Learn.genomeUnits - 2) { 83 | // Get two random Genomes 84 | var genA = _.sample(bestGenomes).toJSON(); 85 | var genB = _.sample(bestGenomes).toJSON(); 86 | 87 | // Cross over and Mutate 88 | var newGenome = Learn.mutate(Learn.crossOver(genA, genB)); 89 | 90 | // Add to generation 91 | Learn.genomes.push(Network.fromJSON(newGenome)); 92 | } 93 | 94 | // Mutation-only 95 | while (Learn.genomes.length < Learn.genomeUnits) { 96 | // Get two random Genomes 97 | var gen = _.sample(bestGenomes).toJSON(); 98 | 99 | // Cross over and Mutate 100 | var newGenome = Learn.mutate(gen); 101 | 102 | // Add to generation 103 | Learn.genomes.push(Network.fromJSON(newGenome)); 104 | } 105 | 106 | Learn.ui.logger.log('Completed generation '+Learn.generation); 107 | 108 | // Execute next generation 109 | Learn.executeGeneration(); 110 | }) 111 | } 112 | 113 | 114 | // Sort all the genomes, and delete the worst one 115 | // untill the genome list has selectN elements. 116 | Learn.selectBestGenomes = function (selectN){ 117 | var selected = _.sortBy(Learn.genomes, 'fitness').reverse(); 118 | 119 | while (selected.length > selectN) { 120 | selected.pop(); 121 | } 122 | 123 | Learn.ui.logger.log('Fitness: '+_.pluck(selected, 'fitness').join(',')); 124 | 125 | return selected; 126 | } 127 | 128 | 129 | // Waits the game to end, and start a new one, then: 130 | // 1) Set's listener for sensorData 131 | // 2) On data read, applyes the neural network, and 132 | // set it's output 133 | // 3) When the game has ended and compute the fitness 134 | Learn.executeGenome = function (genome, next){ 135 | if (Learn.state == 'STOP') { 136 | return; 137 | } 138 | 139 | Learn.genome = Learn.genomes.indexOf(genome) + 1; 140 | // Learn.ui.logger.log('Executing genome '+Learn.genome); 141 | 142 | // Check if genome has AT LEAST some experience 143 | if (Learn.shouldCheckExperience) { 144 | if (!Learn.checkExperience(genome)) { 145 | genome.fitness = 0; 146 | // Learn.ui.logger.log('Genome '+Learn.genome+' has no min. experience'); 147 | return next(); 148 | } 149 | } 150 | 151 | Learn.gm.startNewGame(function (){ 152 | 153 | // Reads sensor data, and apply network 154 | Learn.gm.onSensorData = function (){ 155 | var inputs = [ 156 | Learn.gm.sensors[0].value, 157 | Learn.gm.sensors[0].size, 158 | Learn.gm.sensors[0].speed, 159 | ]; 160 | // console.log(inputs); 161 | // Apply to network 162 | var outputs = genome.activate(inputs); 163 | 164 | Learn.gm.setGameOutput(outputs[0]); 165 | } 166 | 167 | // Wait game end, and compute fitness 168 | Learn.gm.onGameEnd = function (points){ 169 | Learn.ui.logger.log('Genome '+Learn.genome+' ended. Fitness: '+points); 170 | 171 | // Save Genome fitness 172 | genome.fitness = points; 173 | 174 | // Go to next genome 175 | next(); 176 | } 177 | }); 178 | 179 | } 180 | 181 | 182 | // Validate if any acction occur uppon a given input (in this case, distance). 183 | // If genome only keeps a single activation value for any given input, 184 | // it will return false 185 | Learn.checkExperience = function (genome) { 186 | 187 | var step = 0.1, start = 0.0, stop = 1; 188 | 189 | // Inputs are default. We only want to test the first index 190 | var inputs = [0.0, 0.3, 0.2]; 191 | var activation, state, outputs = {}; 192 | 193 | for (var k = start; k < stop; k += step) { 194 | inputs[0] = k; 195 | 196 | activation = genome.activate(inputs); 197 | state = Learn.gm.getDiscreteState(activation); 198 | 199 | outputs[state] = true; 200 | } 201 | 202 | // Count states, and return true if greater than 1 203 | return _.keys(outputs).length > 1; 204 | } 205 | 206 | 207 | // Load genomes saved from JSON file 208 | Learn.loadGenomes = function (genomes, deleteOthers){ 209 | if (deleteOthers) { 210 | Learn.genomes = []; 211 | } 212 | 213 | var loaded = 0; 214 | for (var k in genomes) { 215 | Learn.genomes.push(Network.fromJSON(genomes[k])); 216 | loaded++; 217 | } 218 | 219 | Learn.ui.logger.log('Loaded '+loaded+' genomes!'); 220 | } 221 | 222 | 223 | // Builds a new genome based on the 224 | // expected number of inputs and outputs 225 | Learn.buildGenome = function (inputs, outputs) { 226 | Learn.ui.logger.log('Build genome '+(Learn.genomes.length+1)); 227 | 228 | var network = new Architect.Perceptron(inputs, 4, 4, outputs); 229 | 230 | return network; 231 | } 232 | 233 | 234 | // SPECIFIC to Neural Network. 235 | // Those two methods convert from JSON to Array, and from Array to JSON 236 | Learn.crossOver = function (netA, netB) { 237 | // Swap (50% prob.) 238 | if (Math.random() > 0.5) { 239 | var tmp = netA; 240 | netA = netB; 241 | netB = tmp; 242 | } 243 | 244 | // Clone network 245 | netA = _.cloneDeep(netA); 246 | netB = _.cloneDeep(netB); 247 | 248 | // Cross over data keys 249 | Learn.crossOverDataKey(netA.neurons, netB.neurons, 'bias'); 250 | 251 | return netA; 252 | } 253 | 254 | 255 | // Does random mutations across all 256 | // the biases and weights of the Networks 257 | // (This must be done in the JSON to 258 | // prevent modifying the current one) 259 | Learn.mutate = function (net){ 260 | // Mutate 261 | Learn.mutateDataKeys(net.neurons, 'bias', Learn.mutationProb); 262 | 263 | Learn.mutateDataKeys(net.connections, 'weight', Learn.mutationProb); 264 | 265 | return net; 266 | } 267 | 268 | 269 | // Given an Object A and an object B, both Arrays 270 | // of Objects: 271 | // 272 | // 1) Select a cross over point (cutLocation) 273 | // randomly (going from 0 to A.length) 274 | // 2) Swap values from `key` one to another, 275 | // starting by cutLocation 276 | Learn.crossOverDataKey = function (a, b, key) { 277 | var cutLocation = Math.round(a.length * Math.random()); 278 | 279 | var tmp; 280 | for (var k = cutLocation; k < a.length; k++) { 281 | // Swap 282 | tmp = a[k][key]; 283 | a[k][key] = b[k][key]; 284 | b[k][key] = tmp; 285 | } 286 | } 287 | 288 | 289 | // Given an Array of objects with key `key`, 290 | // and also a `mutationRate`, randomly Mutate 291 | // the value of each key, if random value is 292 | // lower than mutationRate for each element. 293 | Learn.mutateDataKeys = function (a, key, mutationRate){ 294 | for (var k = 0; k < a.length; k++) { 295 | // Should mutate? 296 | if (Math.random() > mutationRate) { 297 | continue; 298 | } 299 | 300 | a[k][key] += a[k][key] * (Math.random() - 0.5) * 3 + (Math.random() - 0.5); 301 | } 302 | } 303 | 304 | 305 | module.exports = Learn; -------------------------------------------------------------------------------- /GameManipulator.js: -------------------------------------------------------------------------------- 1 | var robot = require('robotjs'); 2 | 3 | // Cache screen size 4 | var screenSize = robot.getScreenSize(); 5 | 6 | var Scanner = require ('./Scanner'); 7 | 8 | // COLOR DEFINITIONS 9 | // This is the Dino's colour, also used by Obstacles. 10 | var COLOR_DINOSAUR = '535353'; 11 | var DARK_COLOR_DINO = 'ACACAC'; 12 | 13 | var GameManipulator = { 14 | 15 | // Stores the game position (Globally) 16 | offset: null, 17 | width: null, 18 | 19 | // Stores points (jumps) 20 | points: 0, 21 | 22 | // Listners 23 | onGameEnd: null, 24 | onGameStart: null, 25 | onSensorData: null, 26 | 27 | // Game State 28 | gamestate: 'OVER', 29 | 30 | // GameOver Position 31 | gameOverOffset: [190, -75], 32 | 33 | // Stores an array of "sensors" (Ray tracings) 34 | // Positions are always relative to global "offset" 35 | sensors: [ 36 | { 37 | lastValue: 1, 38 | 39 | value: null, 40 | offset: [84, -15], // 64,-15 41 | step: [4, 0], 42 | length: 0.3, 43 | 44 | // Speed 45 | speed: 0, 46 | lastComputeSpeed: 0, 47 | 48 | // Computes size of the object 49 | size: 0, 50 | computeSize: true, 51 | }, 52 | ] 53 | }; 54 | 55 | 56 | // Find out dinosaur (fast) 57 | GameManipulator.findGamePosition = function () { 58 | var pos, dinoPos, skipXFast = 15; 59 | 60 | for (var x = 20; x < screenSize.width; x+= skipXFast) { 61 | dinoPos = Scanner.scanUntil( 62 | // Start position 63 | [x, 80], 64 | // Skip pixels 65 | [0, skipXFast], 66 | // Searching Color 67 | COLOR_DINOSAUR, 68 | // Normal mode (not inverse) 69 | false, 70 | // Iteration limit 71 | 500 / skipXFast); 72 | 73 | if (dinoPos) { 74 | break; 75 | } 76 | } 77 | 78 | if (!dinoPos) { 79 | return null; 80 | } 81 | 82 | for (var x = dinoPos[0] - 50; x <= dinoPos[0]; x += 1) { 83 | pos = Scanner.scanUntil( 84 | // Start position 85 | [x, dinoPos[1] - 2], 86 | // Skip pixels 87 | [0, 1], 88 | // Searching Color 89 | COLOR_DINOSAUR, 90 | // Normal mode (not inverse) 91 | false, 92 | // Iteration limit 93 | 100); 94 | 95 | if (pos) { 96 | break; 97 | } 98 | } 99 | 100 | // Did actually found? If not, error! 101 | if (!pos) { 102 | return null; 103 | } 104 | 105 | // Find the end of the game 106 | var endPos = pos; 107 | 108 | while (robot.getPixelColor(endPos[0] + 3, endPos[1]) == COLOR_DINOSAUR) { 109 | endPos = Scanner.scanUntil( 110 | // Start position 111 | [endPos[0] + 2, endPos[1]], 112 | // Skip pixels 113 | [2, 0], 114 | // Searching Color 115 | COLOR_DINOSAUR, 116 | // Invert mode 117 | true, 118 | // Iteration limit 119 | 600); 120 | } 121 | 122 | // Did actually found? If not, error! 123 | if (!endPos) { 124 | return null; 125 | } 126 | 127 | // Save to allow global access 128 | GameManipulator.offset = pos; 129 | GameManipulator.width = 600;//endPos[0] - pos[0]; 130 | 131 | return pos; 132 | }; 133 | 134 | 135 | // Read Game state 136 | // (If game is ended or is playing) 137 | GameManipulator.readGameState = function () { 138 | // Read GameOver 139 | var found = Scanner.scanUntil( 140 | [ 141 | GameManipulator.offset[0] + GameManipulator.gameOverOffset[0], 142 | GameManipulator.offset[1] + GameManipulator.gameOverOffset[1] 143 | ], 144 | 145 | [2, 0], COLOR_DINOSAUR, false, 20); 146 | 147 | if (found && GameManipulator.gamestate != 'OVER') { 148 | GameManipulator.gamestate = 'OVER'; 149 | 150 | // Clear keys 151 | GameManipulator.setGameOutput(0.5); 152 | 153 | // Trigger callback and clear 154 | GameManipulator.onGameEnd && GameManipulator.onGameEnd(GameManipulator.points); 155 | GameManipulator.onGameEnd = null; 156 | 157 | // console.log('GAME OVER: '+GameManipulator.points); 158 | 159 | } else if (!found && GameManipulator.gamestate != 'PLAYING') { 160 | GameManipulator.gamestate = 'PLAYING'; 161 | 162 | // Clear points 163 | GameManipulator.points = 0; 164 | GameManipulator.lastScore = 0; 165 | 166 | // Clear keys 167 | GameManipulator.setGameOutput(0.5); 168 | 169 | // Clear sensors 170 | GameManipulator.sensors[0].lastComputeSpeed = 0; 171 | GameManipulator.sensors[0].lastSpeeds = []; 172 | GameManipulator.sensors[0].lastValue = 1; 173 | GameManipulator.sensors[0].value = 1; 174 | GameManipulator.sensors[0].speed = 0; 175 | GameManipulator.sensors[0].size = 0; 176 | 177 | // Clar Output flags 178 | GameManipulator.lastOutputSet = 'NONE'; 179 | 180 | // Trigger callback and clear 181 | GameManipulator.onGameStart && GameManipulator.onGameStart(); 182 | GameManipulator.onGameStart = null; 183 | 184 | // console.log('GAME RUNNING '+GameManipulator.points); 185 | } 186 | } 187 | 188 | 189 | // Call this to start a fresh new game 190 | // Will wait untill game has ended, 191 | // and call the `next` callback 192 | var _startKeyInterval; 193 | GameManipulator.startNewGame = function (next) { 194 | 195 | // Refresh state 196 | GameManipulator.readGameState(); 197 | 198 | // If game is already over, press space 199 | if (GameManipulator.gamestate == 'OVER') { 200 | clearInterval(_startKeyInterval); 201 | 202 | // Set start callback 203 | GameManipulator.onGameStart = function (argument) { 204 | clearInterval(_startKeyInterval); 205 | next && next(); 206 | }; 207 | 208 | // Press space to begin game (repetidelly) 209 | _startKeyInterval = setInterval(function (){ 210 | // Due to dino slowly gliding over the screen after multiple restarts, its better to just reload the page 211 | GameManipulator.reloadPage(); 212 | setTimeout(function() { 213 | // Once reloaded we wait 0.5sec for it to let us start the game with a space. 214 | robot.keyTap(' '); 215 | }, 500); 216 | }, 300); 217 | 218 | // Refresh state 219 | GameManipulator.readGameState(); 220 | 221 | } else { 222 | // Wait die, and call recursive action 223 | GameManipulator.onGameEnd = function () { 224 | GameManipulator.startNewGame(next); 225 | } 226 | } 227 | 228 | 229 | } 230 | 231 | // reload the page 232 | GameManipulator.reloadPage = function () 233 | { 234 | // retrieves platform 235 | var platform = process.platform; 236 | 237 | if(/^win/.test(process.platform)) { 238 | robot.keyTap('r','control'); 239 | } else if(/^darwin/.test(process.platform)) { 240 | robot.keyTap('r','command'); 241 | } 242 | } 243 | 244 | 245 | // Compute points based on sensors 246 | // 247 | // Basicaly, checks if an object has 248 | // passed trough the sensor and the 249 | // value is now higher than before 250 | GameManipulator.computePoints = function () { 251 | for (var k in GameManipulator.sensors) { 252 | var sensor = GameManipulator.sensors[k]; 253 | 254 | if (sensor.value > 0.5 && sensor.lastValue < 0.3) { 255 | GameManipulator.points++; 256 | // console.log('POINTS: '+GameManipulator.points); 257 | } 258 | } 259 | } 260 | 261 | 262 | // Read sensors 263 | // 264 | // Sensors are like ray-traces: 265 | // They have a starting point, 266 | // and a limit to search for. 267 | // 268 | // Each sensor can gatter data about 269 | // the DISTANCE of the object, it's 270 | // SIZE and it's speed 271 | // 272 | // Note: We currently only have a sensor. 273 | GameManipulator.readSensors = function () { 274 | var offset = GameManipulator.offset; 275 | 276 | var startTime = Date.now(); 277 | 278 | for (var k in GameManipulator.sensors) { 279 | 280 | var sensor = GameManipulator.sensors[k]; 281 | 282 | // Calculate absolute position of ray tracing 283 | var start = [ 284 | offset[0] + sensor.offset[0], 285 | offset[1] + sensor.offset[1], 286 | ]; 287 | 288 | // Compute cursor forwarding 289 | var forward = sensor.value * GameManipulator.width * 0.8 * sensor.length; 290 | 291 | var end = Scanner.scanUntil( 292 | // console.log( 293 | // Start position 294 | [start[0], start[1]], 295 | // Skip pixels 296 | sensor.step, 297 | // Searching Color 298 | COLOR_DINOSAUR, 299 | // Invert mode? 300 | false, 301 | // Iteration limit 302 | (GameManipulator.width * sensor.length) / sensor.step[0]); 303 | 304 | // Save lastValue 305 | sensor.lastValue = sensor.value; 306 | 307 | // Calculate the Sensor value 308 | if (end) { 309 | sensor.value = (end[0] - start[0]) / (GameManipulator.width * sensor.length); 310 | 311 | // Calculate size of obstacle 312 | var endPoint = Scanner.scanUntil( 313 | [end[0] + 75, end[1]], 314 | [-2, 0], 315 | COLOR_DINOSAUR, 316 | false, 317 | 75 / 2 318 | ); 319 | 320 | // If no end point, set the start point as end 321 | if (!endPoint) { 322 | endPoint = end; 323 | } 324 | 325 | var sizeTmp = (endPoint[0] - end[0]) / 100.0; 326 | if (GameManipulator.points == sensor.lastScore) { 327 | // It's the same obstacle. Set size to "max" of both 328 | sensor.size = Math.max(sensor.size, sizeTmp); 329 | } else { 330 | sensor.size = sizeTmp; 331 | } 332 | 333 | 334 | // We use the current score to check for object equality 335 | sensor.lastScore = GameManipulator.points; 336 | 337 | // sensor.size = Math.max(sensor.size, endPoint[0] - end[0]); 338 | 339 | } else { 340 | sensor.value = 1; 341 | sensor.size = 0; 342 | } 343 | 344 | // Compute speed 345 | var dt = (Date.now() - sensor.lastComputeSpeed) / 1000; 346 | sensor.lastComputeSpeed = Date.now(); 347 | 348 | if (sensor.value < sensor.lastValue) { 349 | // Compute speed 350 | var newSpeed = (sensor.lastValue - sensor.value) / dt; 351 | 352 | sensor.lastSpeeds.unshift(newSpeed); 353 | 354 | while (sensor.lastSpeeds.length > 10) { 355 | sensor.lastSpeeds.pop(); 356 | } 357 | 358 | // Take Average 359 | var avgSpeed = 0; 360 | for (var k in sensor.lastSpeeds) { 361 | avgSpeed += sensor.lastSpeeds[k] / sensor.lastSpeeds.length; 362 | } 363 | 364 | sensor.speed = Math.max(avgSpeed - 1.5, sensor.speed); 365 | 366 | } 367 | 368 | // Save length/size of sensor value 369 | sensor.size = Math.min(sensor.size, 1.0); 370 | 371 | startTime = Date.now(); 372 | } 373 | 374 | // Compute points 375 | GameManipulator.computePoints(); 376 | 377 | // Call sensor callback (to act) 378 | GameManipulator.onSensorData && GameManipulator.onSensorData(); 379 | } 380 | 381 | 382 | // Set action to game 383 | // Values: 384 | // 0.00 to 0.45: DOWN 385 | // 0.45 to 0.55: NOTHING 386 | // 0.55 to 1.00: UP (JUMP) 387 | var PRESS = 'down'; 388 | var RELEASE = 'up'; 389 | 390 | GameManipulator.lastOutputSet = 'NONE'; 391 | GameManipulator.lastOutputSetTime = 0; 392 | 393 | GameManipulator.setGameOutput = function (output){ 394 | 395 | GameManipulator.gameOutput = output; 396 | GameManipulator.gameOutputString = GameManipulator.getDiscreteState(output); 397 | 398 | if (GameManipulator.gameOutputString == 'DOWN') { 399 | // Skew 400 | robot.keyToggle('up', RELEASE); 401 | robot.keyToggle('down', PRESS); 402 | } else if (GameManipulator.gameOutputString == 'NORM') { 403 | // DO Nothing 404 | robot.keyToggle('up', RELEASE); 405 | robot.keyToggle('down', RELEASE); 406 | } else { 407 | 408 | // Filter JUMP 409 | if (GameManipulator.lastOutputSet != 'JUMP') { 410 | GameManipulator.lastOutputSetTime = Date.now(); 411 | } 412 | 413 | // JUMP 414 | // Check if hasn't jump for more than 3 continuous secconds 415 | if (Date.now() - GameManipulator.lastOutputSetTime < 3000) { 416 | robot.keyToggle('up', PRESS); 417 | robot.keyToggle('down', RELEASE); 418 | } else { 419 | robot.keyToggle('up', RELEASE); 420 | robot.keyToggle('down', RELEASE); 421 | } 422 | 423 | } 424 | 425 | GameManipulator.lastOutputSet = GameManipulator.gameOutputString; 426 | } 427 | 428 | 429 | // 430 | // Simply maps an real number to string actions 431 | // 432 | GameManipulator.getDiscreteState = function (value){ 433 | if (value < 0.45) { 434 | return 'DOWN' 435 | } else if(value > 0.55) { 436 | return 'JUMP'; 437 | } 438 | 439 | return 'NORM'; 440 | } 441 | 442 | 443 | // Click on the Starting point 444 | // to make sure game is focused 445 | GameManipulator.focusGame = function (){ 446 | robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]); 447 | robot.mouseClick('left'); 448 | } 449 | 450 | module.exports = GameManipulator; 451 | -------------------------------------------------------------------------------- /genomes/ninja.json: -------------------------------------------------------------------------------- 1 | [{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.39,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1.3348040246025272,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-2.0322203816959745,"old":-2.0322203816959745,"activation":0.11586127777543492,"bias":-0.3880038627461479,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.13389542952294398,"old":-0.13389542952294398,"activation":0.46657606302794025,"bias":0.4224254883608316,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.3094505597741015,"old":1.3094505597741015,"activation":0.7874212001220087,"bias":1.0005502347416861,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.237310938797589,"old":-1.237310938797589,"activation":0.22490440257338484,"bias":-0.0617127046413693,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.254692958082969,"old":1.254692958082969,"activation":0.77811117738326,"bias":-0.722564815227587,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":-1.5327815896231223,"gater":null},{"from":"0","to":"4","weight":-2.2822599700889814,"gater":null},{"from":"0","to":"5","weight":-3.924941099051849,"gater":null},{"from":"0","to":"6","weight":-1.415126002091672,"gater":null},{"from":"1","to":"3","weight":2.652204887043163,"gater":null},{"from":"1","to":"4","weight":0.25244677897121565,"gater":null},{"from":"1","to":"5","weight":0.6026895028488548,"gater":null},{"from":"1","to":"6","weight":-1.684542508399489,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.49054029626373513,"gater":null},{"from":"2","to":"5","weight":0.05532753689692624,"gater":null},{"from":"2","to":"6","weight":-0.3885414235508119,"gater":null},{"from":"3","to":"7","weight":-2.1244527601868586,"gater":null},{"from":"4","to":"7","weight":2.7318777381342363,"gater":null},{"from":"5","to":"7","weight":1.049037113655114,"gater":null},{"from":"6","to":"7","weight":0.5457285875056297,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1.558682894018903,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0.37855917410895623,"old":0.37855917410895623,"activation":0.5935255463003045,"bias":0.0741467908577419,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-3.095739582607292,"old":-3.095739582607292,"activation":0.04328333546512352,"bias":-0.058910553209155725,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-3.385600747957228,"old":-3.385600747957228,"activation":0.03274852017462801,"bias":0.45310226576518264,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-2.0114726230093747,"old":-2.0114726230093747,"activation":0.11800362278092183,"bias":-0.36206345233458886,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.5020491705615062,"old":-0.5020491705615062,"activation":0.3770592270907349,"bias":-0.15822368844560386,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":3.4322507601634835,"gater":null},{"from":"0","to":"4","weight":-2.8034656130516424,"gater":null},{"from":"0","to":"5","weight":-3.924941099051849,"gater":null},{"from":"0","to":"6","weight":-1.1080729890794625,"gater":null},{"from":"1","to":"3","weight":0.48228087984296164,"gater":null},{"from":"1","to":"4","weight":0.9891307183748549,"gater":null},{"from":"1","to":"5","weight":0.6833116344782653,"gater":null},{"from":"1","to":"6","weight":-0.9599631511320865,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.14971834055661595,"gater":null},{"from":"2","to":"5","weight":0.05532753689692624,"gater":null},{"from":"2","to":"6","weight":-0.34730360079819933,"gater":null},{"from":"3","to":"7","weight":-1.0482693514372388,"gater":null},{"from":"4","to":"7","weight":1.9731384153235008,"gater":null},{"from":"5","to":"7","weight":1.049037113655114,"gater":null},{"from":"6","to":"7","weight":1.343947995818256,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.9870179373832775,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.525730030324892,"old":1.525730030324892,"activation":0.8213807074621803,"bias":0.0741467908577419,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-5.357329475185197,"old":-5.357329475185197,"activation":0.004691364135778572,"bias":-0.058910553209155725,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-3.342436153876341,"old":-3.342436153876341,"activation":0.0341437271323433,"bias":0.45310226576518264,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.9052829198683146,"old":-1.9052829198683146,"activation":0.12951171963332328,"bias":-0.36206345233458886,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.7637087994528299,"old":-0.7637087994528299,"activation":0.31784158786129346,"bias":-0.15822368844560386,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":3.4322507601634835,"gater":null},{"from":"0","to":"4","weight":-5.055376588267963,"gater":null},{"from":"0","to":"5","weight":-3.924941099051849,"gater":null},{"from":"0","to":"6","weight":-1.1080729890794625,"gater":null},{"from":"1","to":"3","weight":-0.36011783279864695,"gater":null},{"from":"1","to":"4","weight":0.8383497771952181,"gater":null},{"from":"1","to":"5","weight":-0.41278719733651026,"gater":null},{"from":"1","to":"6","weight":-0.9599631511320865,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.24623902413811963,"gater":null},{"from":"2","to":"5","weight":0.13110468868822137,"gater":null},{"from":"2","to":"6","weight":-0.4408698788270223,"gater":null},{"from":"3","to":"7","weight":-1.0482693514372388,"gater":null},{"from":"4","to":"7","weight":1.9731384153235008,"gater":null},{"from":"5","to":"7","weight":2.115451057070952,"gater":null},{"from":"6","to":"7","weight":1.343947995818256,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1.3268571659854063,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0.8437682372745674,"old":0.8437682372745674,"activation":0.6992582563012741,"bias":0.0741467908577419,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-3.1891001799837158,"old":-3.1891001799837158,"activation":0.039577968940608936,"bias":-0.058910553209155725,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-3.3984270944786577,"old":-3.3984270944786577,"activation":0.03234465805658589,"bias":0.45310226576518264,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.930958712905677,"old":-1.930958712905677,"activation":0.1266445030749041,"bias":-0.36206345233458886,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.6090075035297202,"old":-0.6090075035297202,"activation":0.3522856330109004,"bias":-0.15822368844560386,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":3.4322507601634835,"gater":null},{"from":"0","to":"4","weight":-2.8034656130516424,"gater":null},{"from":"0","to":"5","weight":-3.924941099051849,"gater":null},{"from":"0","to":"6","weight":-1.1080729890794625,"gater":null},{"from":"1","to":"3","weight":0.48228087984296164,"gater":null},{"from":"1","to":"4","weight":0.29658182092604235,"gater":null},{"from":"1","to":"5","weight":0.6833116344782653,"gater":null},{"from":"1","to":"6","weight":-0.9599631511320865,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.24623902413811963,"gater":null},{"from":"2","to":"5","weight":0.05532753689692624,"gater":null},{"from":"2","to":"6","weight":-0.34730360079819933,"gater":null},{"from":"3","to":"7","weight":-1.0482693514372388,"gater":null},{"from":"4","to":"7","weight":1.9731384153235008,"gater":null},{"from":"5","to":"7","weight":1.049037113655114,"gater":null},{"from":"6","to":"7","weight":1.343947995818256,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.7034429459478244,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-2.7498127565519295,"old":-2.7498127565519295,"activation":0.060097225851886135,"bias":0.19458106718681561,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-2.2049015928380302,"old":-2.2049015928380302,"activation":0.09931118719847756,"bias":0.4224254883608316,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":2.624370964018958,"old":2.624370964018958,"activation":0.9324136787936003,"bias":2.325056714187551,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.2319174138576585,"old":-1.2319174138576585,"activation":0.2258460096643315,"bias":-0.0617127046413693,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.3918628114825984,"old":-0.3918628114825984,"activation":0.40326894730699053,"bias":-0.722564815227587,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":-1.5327815896231223,"gater":null},{"from":"0","to":"4","weight":-2.2822599700889814,"gater":null},{"from":"0","to":"5","weight":0.2603944842845962,"gater":null},{"from":"0","to":"6","weight":-1.415126002091672,"gater":null},{"from":"1","to":"3","weight":2.652204887043163,"gater":null},{"from":"1","to":"4","weight":0.25244677897121565,"gater":null},{"from":"1","to":"5","weight":0.6026895028488548,"gater":null},{"from":"1","to":"6","weight":-1.684542508399489,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.49054029626373513,"gater":null},{"from":"2","to":"5","weight":0.05532753689692624,"gater":null},{"from":"2","to":"6","weight":0.34817506421274,"gater":null},{"from":"3","to":"7","weight":-2.1244527601868586,"gater":null},{"from":"4","to":"7","weight":4.5683114530682865,"gater":null},{"from":"5","to":"7","weight":-0.1271531503187604,"gater":null},{"from":"6","to":"7","weight":0.5457285875056297,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.36315197142666866,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":2.3155029834125243,"old":2.170808356066037,"activation":0.9101528765086756,"bias":-0.3880038627461479,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-4.722373286965069,"old":-4.450745127336467,"activation":0.008815638668693445,"bias":0.4224254883608316,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-2.8767799381497556,"old":-2.867988592331429,"activation":0.05331342290125554,"bias":1.0005502347416861,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.1651050177720992,"old":-1.3228741722854298,"activation":0.23774091470999323,"bias":-0.0617127046413693,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.2583321303399948,"old":-1.2742757519845853,"activation":0.22126114030677738,"bias":-0.611772379000412,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":3.4322507601634835,"gater":null},{"from":"0","to":"4","weight":-5.055376588267963,"gater":null},{"from":"0","to":"5","weight":-3.924941099051849,"gater":null},{"from":"0","to":"6","weight":-1.1080729890794625,"gater":null},{"from":"1","to":"3","weight":-0.36011783279864695,"gater":null},{"from":"1","to":"4","weight":0.8383497771952181,"gater":null},{"from":"1","to":"5","weight":-0.41278719733651026,"gater":null},{"from":"1","to":"6","weight":-0.9599631511320865,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.24623902413811963,"gater":null},{"from":"2","to":"5","weight":0.13110468868822137,"gater":null},{"from":"2","to":"6","weight":0.012889028057163576,"gater":null},{"from":"3","to":"7","weight":-1.177175249553277,"gater":null},{"from":"4","to":"7","weight":1.9731384153235008,"gater":null},{"from":"5","to":"7","weight":1.6495618223150093,"gater":null},{"from":"6","to":"7","weight":1.343947995818256,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1.1559255305454421,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.1867799757167377,"old":1.1867799757167377,"activation":0.7661646708729688,"bias":0.0741467908577419,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-3.4553309099312908,"old":-3.4553309099312908,"activation":0.03061027729003264,"bias":-0.058910553209155725,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-8.175422133392143,"old":-8.175422133392143,"activation":0.0002814083948202581,"bias":0.45310226576518264,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.8715935404270523,"old":-1.8715935404270523,"activation":0.1333574442814589,"bias":-0.36206345233458886,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.7215224304655379,"old":-0.7215224304655379,"activation":0.327057821503504,"bias":-0.15822368844560386,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":3.4322507601634835,"gater":null},{"from":"0","to":"4","weight":-2.8034656130516424,"gater":null},{"from":"0","to":"5","weight":-8.692478911598677,"gater":null},{"from":"0","to":"6","weight":-1.1080729890794625,"gater":null},{"from":"1","to":"3","weight":0.48228087984296164,"gater":null},{"from":"1","to":"4","weight":0.9891307183748549,"gater":null},{"from":"1","to":"5","weight":0.6833116344782653,"gater":null},{"from":"1","to":"6","weight":-0.9599631511320865,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.5129696749501651,"gater":null},{"from":"2","to":"5","weight":0.05532753689692624,"gater":null},{"from":"2","to":"6","weight":-0.34730360079819933,"gater":null},{"from":"3","to":"7","weight":-1.0482693514372388,"gater":null},{"from":"4","to":"7","weight":1.9731384153235008,"gater":null},{"from":"5","to":"7","weight":0.7974766216988861,"gater":null},{"from":"6","to":"7","weight":1.343947995818256,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.7734451448094388,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.9543105834573848,"old":1.6006342385163317,"activation":0.8759159068707985,"bias":0.0741467908577419,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-2.9697160943594763,"old":-1.7265227760704982,"activation":0.04881290310710323,"bias":-0.058910553209155725,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-3.4290460184994735,"old":-2.1725727708991007,"activation":0.03139993365352594,"bias":0.45310226576518264,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.8899481544815321,"old":-1.3307081309467792,"activation":0.2911205266447669,"bias":0.4867451184101333,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.5559143296758808,"old":-0.34429100554572833,"activation":0.3644933310029331,"bias":-0.15822368844560386,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":3.4322507601634835,"gater":null},{"from":"0","to":"4","weight":-2.8034656130516424,"gater":null},{"from":"0","to":"5","weight":-3.924941099051849,"gater":null},{"from":"0","to":"6","weight":-1.1080729890794625,"gater":null},{"from":"1","to":"3","weight":0.48228087984296164,"gater":null},{"from":"1","to":"4","weight":0.9891307183748549,"gater":null},{"from":"1","to":"5","weight":0.6833116344782653,"gater":null},{"from":"1","to":"6","weight":-0.9599631511320865,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.13878156559522326,"gater":null},{"from":"2","to":"5","weight":0.05532753689692624,"gater":null},{"from":"2","to":"6","weight":-0.34730360079819933,"gater":null},{"from":"3","to":"7","weight":-1.0482693514372388,"gater":null},{"from":"4","to":"7","weight":1.9731384153235008,"gater":null},{"from":"5","to":"7","weight":1.049037113655114,"gater":null},{"from":"6","to":"7","weight":1.343947995818256,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.29708918360200975,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-2.516959923393286,"old":-2.516959923393286,"activation":0.07467774600297021,"bias":-0.3880038627461479,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-2.005568697869031,"old":-2.005568697869031,"activation":0.11861948318220022,"bias":0.4224254883608316,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-4.038510636440209,"old":-4.038510636440209,"activation":0.017318485172809074,"bias":1.0005502347416861,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.4725320654496081,"old":-1.4725320654496081,"activation":0.18655805753140917,"bias":0.05802539096033746,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.020211851205492737,"old":-0.020211851205492737,"activation":0.49494720921084584,"bias":-0.722564815227587,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":-1.5327815896231223,"gater":null},{"from":"0","to":"4","weight":-2.2822599700889814,"gater":null},{"from":"0","to":"5","weight":-5.055498083949312,"gater":null},{"from":"0","to":"6","weight":-1.415126002091672,"gater":null},{"from":"1","to":"3","weight":6.240821060311731,"gater":null},{"from":"1","to":"4","weight":0.25244677897121565,"gater":null},{"from":"1","to":"5","weight":0.6026895028488548,"gater":null},{"from":"1","to":"6","weight":0.7046441596613022,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.49054029626373513,"gater":null},{"from":"2","to":"5","weight":0.05532753689692624,"gater":null},{"from":"2","to":"6","weight":-0.3885414235508119,"gater":null},{"from":"3","to":"7","weight":-2.1244527601868586,"gater":null},{"from":"4","to":"7","weight":6.247072477810807,"gater":null},{"from":"5","to":"7","weight":1.049037113655114,"gater":null},{"from":"6","to":"7","weight":0.5457285875056297,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.044444444444444446,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.39,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":1.105168862099406,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.1773801403193218,"old":-1.1773801403193218,"activation":0.23552358090977477,"bias":0.0741467908577419,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.503607985408148,"old":-0.503607985408148,"activation":0.3766931542116663,"bias":-0.058910553209155725,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0.14975469718047513,"old":0.14975469718047513,"activation":0.5373688627468657,"bias":0.45310226576518264,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.5113334025363758,"old":-1.5113334025363758,"activation":0.18074126703206564,"bias":-0.36206345233458886,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.0328527892054404,"old":1.0328527892054404,"activation":0.7374685945972644,"bias":-0.15822368844560386,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":-1.5327815896231223,"gater":null},{"from":"0","to":"4","weight":-2.2822599700889814,"gater":null},{"from":"0","to":"5","weight":-3.924941099051849,"gater":null},{"from":"0","to":"6","weight":-1.415126002091672,"gater":null},{"from":"1","to":"3","weight":2.652204887043163,"gater":null},{"from":"1","to":"4","weight":0.5099133468848835,"gater":null},{"from":"1","to":"5","weight":0.6026895028488548,"gater":null},{"from":"1","to":"6","weight":-1.684542508399489,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.49054029626373513,"gater":null},{"from":"2","to":"5","weight":-0.3293203966856767,"gater":null},{"from":"2","to":"6","weight":-0.3885414235508119,"gater":null},{"from":"3","to":"7","weight":-2.1244527601868586,"gater":null},{"from":"4","to":"7","weight":2.7318777381342363,"gater":null},{"from":"5","to":"7","weight":1.049037113655114,"gater":null},{"from":"6","to":"7","weight":0.5457285875056297,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.47,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.3542686948738374,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.0995689724263564,"old":1.0995689724263564,"activation":0.7501793352992012,"bias":-0.3880038627461479,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0.367292403936924,"old":0.367292403936924,"activation":0.5908045661120014,"bias":0.4224254883608316,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.303415115367706,"old":1.303415115367706,"activation":0.7864091803004385,"bias":1.0005502347416861,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.9910957466148982,"old":-0.9910957466148982,"activation":0.27069570151456196,"bias":-0.0617127046413693,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0.2704192664959591,"old":0.2704192664959591,"activation":0.5671958313821571,"bias":-0.722564815227587,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":-1.5327815896231223,"gater":null},{"from":"0","to":"4","weight":-2.2822599700889814,"gater":null},{"from":"0","to":"5","weight":-3.924941099051849,"gater":null},{"from":"0","to":"6","weight":-1.415126002091672,"gater":null},{"from":"1","to":"3","weight":3.874135168187089,"gater":null},{"from":"1","to":"4","weight":0.25244677897121565,"gater":null},{"from":"1","to":"5","weight":0.6026895028488548,"gater":null},{"from":"1","to":"6","weight":-1.684542508399489,"gater":null},{"from":"2","to":"3","weight":-0.9407286014760969,"gater":null},{"from":"2","to":"4","weight":-0.49054029626373513,"gater":null},{"from":"2","to":"5","weight":0.05532753689692624,"gater":null},{"from":"2","to":"6","weight":-0.3885414235508119,"gater":null},{"from":"3","to":"7","weight":-2.1244527601868586,"gater":null},{"from":"4","to":"7","weight":2.7318777381342363,"gater":null},{"from":"5","to":"7","weight":1.049037113655114,"gater":null},{"from":"6","to":"7","weight":0.5457285875056297,"gater":null}]},{"neurons":[{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.9333333333333333,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.19,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":0,"old":0,"activation":0.6163232373079741,"bias":0,"layer":"input","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":1.931097439250038,"old":1.931097439250038,"activation":0.8733708400510548,"bias":0.0741467908577419,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-4.408568275384055,"old":-4.408568275384055,"activation":0.012026203572561341,"bias":0.30225958184064755,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-3.6276278239330852,"old":-3.6276278239330852,"activation":0.025890999120548548,"bias":0.45310226576518264,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-1.8503762584740953,"old":-1.8503762584740953,"activation":0.13582872600720639,"bias":-0.36206345233458886,"layer":"0","squash":"LOGISTIC"},{"trace":{"elegibility":{},"extended":{}},"state":-0.9132444780298036,"old":-0.9132444780298036,"activation":0.2863363757721577,"bias":-0.15822368844560386,"layer":"output","squash":"LOGISTIC"}],"connections":[{"from":"0","to":"3","weight":3.4322507601634835,"gater":null},{"from":"0","to":"4","weight":-5.055376588267963,"gater":null},{"from":"0","to":"5","weight":-4.240012049192514,"gater":null},{"from":"0","to":"6","weight":-1.1080729890794625,"gater":null},{"from":"1","to":"3","weight":-0.5773469665865629,"gater":null},{"from":"1","to":"4","weight":0.8383497771952181,"gater":null},{"from":"1","to":"5","weight":-0.41278719733651026,"gater":null},{"from":"1","to":"6","weight":-0.9599631511320865,"gater":null},{"from":"2","to":"3","weight":-2.006718870730313,"gater":null},{"from":"2","to":"4","weight":-0.24623902413811963,"gater":null},{"from":"2","to":"5","weight":-0.07294215151508224,"gater":null},{"from":"2","to":"6","weight":-0.4408698788270223,"gater":null},{"from":"3","to":"7","weight":-1.0482693514372388,"gater":null},{"from":"4","to":"7","weight":1.9731384153235008,"gater":null},{"from":"5","to":"7","weight":5.232172235513767,"gater":null},{"from":"6","to":"7","weight":0.009656009528346443,"gater":null}]}] --------------------------------------------------------------------------------