Following Felix's style guideline

Changed indent to spaces. Fixed comments. Fixed quotes. Added guideline
to README. Added space to while/if/for. Added bracket in all
statements. Removed extra comment lines.
Esse commit está contido em:
Ivan Seidel
2015-12-29 13:47:46 -02:00
commit b062da9296
6 arquivos alterados com 726 adições e 792 exclusões
+282 -306
Ver Arquivo
@@ -1,268 +1,246 @@
/*
Developed by Ivan Seidel [https://github.com/ivanseidel]
*/
var robot = require('robotjs'); var robot = require('robotjs');
var scanner = require ('./scanner'); // Cache screen size
var screenSize = robot.getScreenSize(); var screenSize = robot.getScreenSize();
// var Scanner = require ('./Scanner');
// Color definitions
//
// COLOR DEFINITIONS
// This is the Dino's colour, also used by Obstacles. // This is the Dino's colour, also used by Obstacles.
var COLOR_DINOSAUR = "535353"; var COLOR_DINOSAUR = '535353';
var GameManipulator = { var GameManipulator = {
// Stores the game position (Globally) // Stores the game position (Globally)
offset: null, offset: null,
width: null, width: null,
// Stores points (jumps) // Stores points (jumps)
points: 0, points: 0,
// Listners // Listners
onGameEnd: null, onGameEnd: null,
onGameStart: null, onGameStart: null,
onSensorData: null, onSensorData: null,
// Game State // Game State
gamestate: 'OVER', gamestate: 'OVER',
// GameOver Position // GameOver Position
gameOverOffset: [190, -82], gameOverOffset: [190, -82],
// // Stores an array of "sensors" (Ray tracings)
// Stores an array of "sensors" (Ray tracings) // Positions are always relative to global "offset"
// Positions are always relative to global "offset" sensors: [
// {
sensors: [ lastValue: 1,
{
lastValue: 1,
value: null, value: null,
offset: [84, -15], // 64,-15 offset: [84, -15], // 64,-15
step: [4, 0], step: [4, 0],
length: 0.3, length: 0.3,
// Speed // Speed
speed: 0, speed: 0,
lastComputeSpeed: 0, lastComputeSpeed: 0,
// Computes size of the object // Computes size of the object
size: 0, size: 0,
computeSize: true, computeSize: true,
}, },
]
// {
// lastValue: 1,
// value: null,
// offset: [66, -30],
// step: [5, 0],
// length: 0.5,
// }
]
}; };
//
// Find out dinosaur (fast) // Find out dinosaur (fast)
//
GameManipulator.findGamePosition = function () { GameManipulator.findGamePosition = function () {
var pos, dinoPos, skipXFast = 15; var pos, dinoPos, skipXFast = 15;
for(var x = 20; x < screenSize.width; x+= skipXFast){ for (var x = 20; x < screenSize.width; x+= skipXFast) {
dinoPos = Scanner.scanUntil(
// Start position
[x, 80],
// Skip pixels
[0, skipXFast],
// Searching Color
COLOR_DINOSAUR,
// Normal mode (not inverse)
false,
// Iteration limit
500 / skipXFast);
dinoPos = scanner.scanUntil( if (dinoPos) {
// Start position break;
[x, 80], }
// Skip pixels }
[0, skipXFast],
// Searching Color
COLOR_DINOSAUR,
// Normal mode (not inverse)
false,
// Iteration limit
500 / skipXFast);
if(dinoPos) if (!dinoPos) {
break; return null;
} }
if(!dinoPos) for (var x = dinoPos[0] - 50; x <= dinoPos[0]; x += 1) {
return null; pos = Scanner.scanUntil(
// Start position
[x, dinoPos[1] - 2],
// Skip pixels
[0, 1],
// Searching Color
COLOR_DINOSAUR,
// Normal mode (not inverse)
false,
// Iteration limit
100);
for(var x = dinoPos[0] - 50; x <= dinoPos[0]; x += 1){ if (pos) {
break;
}
}
pos = scanner.scanUntil( // Did actually found? If not, error!
// Start position if (!pos) {
[x, dinoPos[1] - 2], return null;
// Skip pixels }
[0, 1],
// Searching Color
COLOR_DINOSAUR,
// Normal mode (not inverse)
false,
// Iteration limit
100);
if(pos) // Find the end of the game
break; var endPos = pos;
}
// Did actually found? If not, error! while (robot.getPixelColor(endPos[0] + 3, endPos[1]) == COLOR_DINOSAUR) {
if(!pos) endPos = Scanner.scanUntil(
return null; // Start position
[endPos[0] + 2, endPos[1]],
// Skip pixels
[2, 0],
// Searching Color
COLOR_DINOSAUR,
// Invert mode
true,
// Iteration limit
600);
}
// Did actually found? If not, error!
if (!endPos) {
return null;
}
// Find the end of the game // Save to allow global access
var endPos = pos; GameManipulator.offset = pos;
GameManipulator.width = 600;//endPos[0] - pos[0];
while(robot.getPixelColor(endPos[0] + 3, endPos[1]) == COLOR_DINOSAUR){ return pos;
endPos = scanner.scanUntil(
// Start position
[endPos[0] + 2, endPos[1]],
// Skip pixels
[2, 0],
// Searching Color
COLOR_DINOSAUR,
// Invert mode
true,
// Iteration limit
600);
}
// Did actually found? If not, error!
if(!endPos)
return null;
// Save to allow global access
GameManipulator.offset = pos;
GameManipulator.width = 600;//endPos[0] - pos[0];
return pos;
}; };
//
// Read Game state // Read Game state
// (If game is ended or is playing) // (If game is ended or is playing)
//
GameManipulator.readGameState = function () { GameManipulator.readGameState = function () {
// Read GameOver // Read GameOver
var found = scanner.scanUntil( var found = Scanner.scanUntil(
[ [
GameManipulator.offset[0] + GameManipulator.gameOverOffset[0], GameManipulator.offset[0] + GameManipulator.gameOverOffset[0],
GameManipulator.offset[1] + GameManipulator.gameOverOffset[1] GameManipulator.offset[1] + GameManipulator.gameOverOffset[1]
], ],
[2, 0], COLOR_DINOSAUR, false, 20); [2, 0], COLOR_DINOSAUR, false, 20);
if(found && GameManipulator.gamestate != 'OVER'){ if (found && GameManipulator.gamestate != 'OVER') {
GameManipulator.gamestate = 'OVER'; GameManipulator.gamestate = 'OVER';
// Clear keys // Clear keys
GameManipulator.setGameOutput(0.5); GameManipulator.setGameOutput(0.5);
// Trigger callback and clear // Trigger callback and clear
GameManipulator.onGameEnd && GameManipulator.onGameEnd(GameManipulator.points); GameManipulator.onGameEnd && GameManipulator.onGameEnd(GameManipulator.points);
GameManipulator.onGameEnd = null; GameManipulator.onGameEnd = null;
// console.log('GAME OVER: '+GameManipulator.points); // console.log('GAME OVER: '+GameManipulator.points);
}else if(!found && GameManipulator.gamestate != 'PLAYING'){ } else if (!found && GameManipulator.gamestate != 'PLAYING') {
GameManipulator.gamestate = 'PLAYING'; GameManipulator.gamestate = 'PLAYING';
// Clear points // Clear points
GameManipulator.points = 0; GameManipulator.points = 0;
GameManipulator.lastScore = 0; GameManipulator.lastScore = 0;
// Clear keys // Clear keys
GameManipulator.setGameOutput(0.5); GameManipulator.setGameOutput(0.5);
// Clear sensors // Clear sensors
GameManipulator.sensors[0].lastComputeSpeed = 0; GameManipulator.sensors[0].lastComputeSpeed = 0;
GameManipulator.sensors[0].lastSpeeds = []; GameManipulator.sensors[0].lastSpeeds = [];
GameManipulator.sensors[0].lastValue = 1; GameManipulator.sensors[0].lastValue = 1;
GameManipulator.sensors[0].value = 1; GameManipulator.sensors[0].value = 1;
GameManipulator.sensors[0].speed = 0; GameManipulator.sensors[0].speed = 0;
GameManipulator.sensors[0].size = 0; GameManipulator.sensors[0].size = 0;
// Clar Output flags // Clar Output flags
GameManipulator.lastOutputSet = 'NONE'; GameManipulator.lastOutputSet = 'NONE';
// Trigger callback and clear // Trigger callback and clear
GameManipulator.onGameStart && GameManipulator.onGameStart(); GameManipulator.onGameStart && GameManipulator.onGameStart();
GameManipulator.onGameStart = null; GameManipulator.onGameStart = null;
// console.log('GAME RUNNING '+GameManipulator.points); // console.log('GAME RUNNING '+GameManipulator.points);
} }
} }
//
// Call this to start a fresh new game // Call this to start a fresh new game
// Will wait untill game has ended, // Will wait untill game has ended,
// and call the `next` callback // and call the `next` callback
//
var _startKeyInterval; var _startKeyInterval;
GameManipulator.startNewGame = function (next) { GameManipulator.startNewGame = function (next) {
// Refresh state // Refresh state
GameManipulator.readGameState(); GameManipulator.readGameState();
// If game is already over, press space // If game is already over, press space
if(GameManipulator.gamestate == 'OVER'){ if (GameManipulator.gamestate == 'OVER') {
clearInterval(_startKeyInterval); clearInterval(_startKeyInterval);
// Set start callback // Set start callback
GameManipulator.onGameStart = function (argument) { GameManipulator.onGameStart = function (argument) {
clearInterval(_startKeyInterval); clearInterval(_startKeyInterval);
next && next(); next && next();
}; };
// Press space to begin game (repetidelly) // Press space to begin game (repetidelly)
_startKeyInterval = setInterval(function (){ _startKeyInterval = setInterval(function (){
robot.keyTap(' '); robot.keyTap(' ');
}, 300); }, 300);
// Refresh state // Refresh state
GameManipulator.readGameState(); GameManipulator.readGameState();
}else{ } else {
// Wait die, and call recursive action // Wait die, and call recursive action
GameManipulator.onGameEnd = function () { GameManipulator.onGameEnd = function () {
GameManipulator.startNewGame(next); GameManipulator.startNewGame(next);
} }
} }
} }
//
// Compute points based on sensors // Compute points based on sensors
// //
// Basicaly, checks if an object has // Basicaly, checks if an object has
// passed trough the sensor and the // passed trough the sensor and the
// value is now higher than before // value is now higher than before
//
GameManipulator.computePoints = function () { GameManipulator.computePoints = function () {
for(var k in GameManipulator.sensors){ for (var k in GameManipulator.sensors) {
var sensor = GameManipulator.sensors[k]; var sensor = GameManipulator.sensors[k];
if(sensor.value > 0.5 && sensor.lastValue < 0.3){ if (sensor.value > 0.5 && sensor.lastValue < 0.3) {
GameManipulator.points++; GameManipulator.points++;
// console.log('POINTS: '+GameManipulator.points); // console.log('POINTS: '+GameManipulator.points);
} }
} }
} }
//
// Read sensors // Read sensors
// //
// Sensors are like ray-traces: // Sensors are like ray-traces:
@@ -274,183 +252,181 @@ GameManipulator.computePoints = function () {
// SIZE and it's speed // SIZE and it's speed
// //
// Note: We currently only have a sensor. // Note: We currently only have a sensor.
//
GameManipulator.readSensors = function () { GameManipulator.readSensors = function () {
var offset = GameManipulator.offset; var offset = GameManipulator.offset;
var startTime = Date.now(); var startTime = Date.now();
for(var k in GameManipulator.sensors){ for (var k in GameManipulator.sensors) {
var sensor = GameManipulator.sensors[k]; var sensor = GameManipulator.sensors[k];
// Calculate absolute position of ray tracing // Calculate absolute position of ray tracing
var start = [ var start = [
offset[0] + sensor.offset[0], offset[0] + sensor.offset[0],
offset[1] + sensor.offset[1], offset[1] + sensor.offset[1],
]; ];
// Compute cursor forwarding // Compute cursor forwarding
var forward = sensor.value * GameManipulator.width * 0.8 * sensor.length; var forward = sensor.value * GameManipulator.width * 0.8 * sensor.length;
var end = scanner.scanUntil( var end = Scanner.scanUntil(
// console.log( // console.log(
// Start position // Start position
[start[0], start[1]], [start[0], start[1]],
// Skip pixels // Skip pixels
sensor.step, sensor.step,
// Searching Color // Searching Color
COLOR_DINOSAUR, COLOR_DINOSAUR,
// Invert mode? // Invert mode?
false, false,
// Iteration limit // Iteration limit
(GameManipulator.width * sensor.length) / sensor.step[0]); (GameManipulator.width * sensor.length) / sensor.step[0]);
// Save lastValue // Save lastValue
sensor.lastValue = sensor.value; sensor.lastValue = sensor.value;
// Calculate the Sensor value // Calculate the Sensor value
if(end){ if (end) {
sensor.value = (end[0] - start[0]) / (GameManipulator.width * sensor.length); sensor.value = (end[0] - start[0]) / (GameManipulator.width * sensor.length);
// // Calculate size of obstacle
// Calculate size of obstacle var endPoint = Scanner.scanUntil(
// [end[0] + 75, end[1]],
var endPoint = scanner.scanUntil( [-2, 0],
[end[0] + 75, end[1]], COLOR_DINOSAUR,
[-2, 0], false,
COLOR_DINOSAUR, 75 / 2
false, );
75 / 2
);
// If no end point, set the start point as end // If no end point, set the start point as end
if(!endPoint) if (!endPoint) {
endPoint = end; endPoint = end;
}
var sizeTmp = (endPoint[0] - end[0]) / 100.0; var sizeTmp = (endPoint[0] - end[0]) / 100.0;
if(GameManipulator.points == sensor.lastScore){ if (GameManipulator.points == sensor.lastScore) {
// It's the same obstacle. Set size to "max" of both // It's the same obstacle. Set size to "max" of both
sensor.size = Math.max(sensor.size, sizeTmp); sensor.size = Math.max(sensor.size, sizeTmp);
}else{ } else {
sensor.size = sizeTmp; sensor.size = sizeTmp;
} }
// We use the current score to check for object equality // We use the current score to check for object equality
sensor.lastScore = GameManipulator.points; sensor.lastScore = GameManipulator.points;
// sensor.size = Math.max(sensor.size, endPoint[0] - end[0]); // sensor.size = Math.max(sensor.size, endPoint[0] - end[0]);
}else{ } else {
sensor.value = 1; sensor.value = 1;
sensor.size = 0; sensor.size = 0;
} }
// Compute speed // Compute speed
var dt = (Date.now() - sensor.lastComputeSpeed) / 1000; var dt = (Date.now() - sensor.lastComputeSpeed) / 1000;
sensor.lastComputeSpeed = Date.now(); sensor.lastComputeSpeed = Date.now();
if(sensor.value < sensor.lastValue){ if (sensor.value < sensor.lastValue) {
// Compute speed // Compute speed
var newSpeed = (sensor.lastValue - sensor.value) / dt; var newSpeed = (sensor.lastValue - sensor.value) / dt;
sensor.lastSpeeds.unshift(newSpeed); sensor.lastSpeeds.unshift(newSpeed);
while(sensor.lastSpeeds.length > 10) while (sensor.lastSpeeds.length > 10) {
sensor.lastSpeeds.pop(); sensor.lastSpeeds.pop();
}
// Take Average // Take Average
var avgSpeed = 0; var avgSpeed = 0;
for(var k in sensor.lastSpeeds) for (var k in sensor.lastSpeeds) {
avgSpeed += sensor.lastSpeeds[k] / sensor.lastSpeeds.length; avgSpeed += sensor.lastSpeeds[k] / sensor.lastSpeeds.length;
}
sensor.speed = Math.max(avgSpeed - 1.5, sensor.speed); sensor.speed = Math.max(avgSpeed - 1.5, sensor.speed);
} }
sensor.size = Math.min(sensor.size, 1.0); // Save length/size of sensor value
sensor.size = Math.min(sensor.size, 1.0);
// if(GameManipulator.gamestate == 'PLAYING') startTime = Date.now();
// console.log("S["+k+"]: ", sensor.value, sensor.size, Date.now() - startTime); }
startTime = Date.now(); // Compute points
} GameManipulator.computePoints();
// Compute points // Call sensor callback (to act)
GameManipulator.computePoints(); GameManipulator.onSensorData && GameManipulator.onSensorData();
// Call sensor callback (to act)
GameManipulator.onSensorData && GameManipulator.onSensorData();
} }
//
// Set action to game // Set action to game
// Values: // Values:
// 0.0 to 0.4: DOWN // 0.00 to 0.45: DOWN
// 0.4 to 0.6: NOTHING // 0.45 to 0.55: NOTHING
// 0.6 to 1.0: UP (JUMP) // 0.55 to 1.00: UP (JUMP)
//
var PRESS = 'down'; var PRESS = 'down';
var RELEASE = 'up'; var RELEASE = 'up';
GameManipulator.lastOutputSet = 'NONE'; GameManipulator.lastOutputSet = 'NONE';
GameManipulator.lastOutputSetTime = 0; GameManipulator.lastOutputSetTime = 0;
GameManipulator.setGameOutput = function (output){ GameManipulator.setGameOutput = function (output){
GameManipulator.gameOutput = output; GameManipulator.gameOutput = output;
GameManipulator.gameOutputString = GameManipulator.getDiscreteState(output); GameManipulator.gameOutputString = GameManipulator.getDiscreteState(output);
if(GameManipulator.gameOutputString == 'DOWN'){ if (GameManipulator.gameOutputString == 'DOWN') {
// Skew // Skew
robot.keyToggle('up', RELEASE); robot.keyToggle('up', RELEASE);
robot.keyToggle('down', PRESS); robot.keyToggle('down', PRESS);
}else if(GameManipulator.gameOutputString == 'NORM'){ } else if (GameManipulator.gameOutputString == 'NORM') {
// DO Nothing // DO Nothing
robot.keyToggle('up', RELEASE); robot.keyToggle('up', RELEASE);
robot.keyToggle('down', RELEASE); robot.keyToggle('down', RELEASE);
}else{ } else {
// Filter JUMP // Filter JUMP
if(GameManipulator.lastOutputSet != 'JUMP') if (GameManipulator.lastOutputSet != 'JUMP') {
GameManipulator.lastOutputSetTime = Date.now(); GameManipulator.lastOutputSetTime = Date.now();
}
// JUMP // JUMP
// Check if hasn't jump for more than 3 continuous secconds // Check if hasn't jump for more than 3 continuous secconds
if(Date.now() - GameManipulator.lastOutputSetTime < 3000){ if (Date.now() - GameManipulator.lastOutputSetTime < 3000) {
robot.keyToggle('up', PRESS); robot.keyToggle('up', PRESS);
robot.keyToggle('down', RELEASE); robot.keyToggle('down', RELEASE);
}else{ } else {
robot.keyToggle('up', RELEASE); robot.keyToggle('up', RELEASE);
robot.keyToggle('down', RELEASE); robot.keyToggle('down', RELEASE);
} }
} }
GameManipulator.lastOutputSet = GameManipulator.gameOutputString; GameManipulator.lastOutputSet = GameManipulator.gameOutputString;
} }
// //
// Simply maps an real number to string actions // Simply maps an real number to string actions
// //
GameManipulator.getDiscreteState = function (value){ GameManipulator.getDiscreteState = function (value){
if(value < 0.45){ if (value < 0.45) {
return 'DOWN' return 'DOWN'
}else if(value > 0.55){ } else if(value > 0.55) {
return 'JUMP'; return 'JUMP';
} }
return 'NORM'; return 'NORM';
} }
//
// Click on the Starting point // Click on the Starting point
// to make sure game is focused // to make sure game is focused
//
GameManipulator.focusGame = function (){ GameManipulator.focusGame = function (){
robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]); robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]);
robot.mouseClick("left"); robot.mouseClick('left');
} }
module.exports = GameManipulator; module.exports = GameManipulator;
+171 -197
Ver Arquivo
@@ -1,72 +1,57 @@
/*
Developed by Ivan Seidel [https://github.com/ivanseidel]
*/
var _ = require('lodash');
var async = require('async');
var synaptic = require('synaptic'); var synaptic = require('synaptic');
var async = require('async');
var _ = require('lodash');
var Network = synaptic.Network;
var Architect = synaptic.Architect; var Architect = synaptic.Architect;
var Network = synaptic.Network;
var Learn = { var Learn = {
//
// Current Genomes
// In form of: {
// fitness: null,
// genome: [...],
// network: {NeuralNetworkObject}
// }
//
genomes: [],
state: 'STOP', // Array of networks for current Genomes
// (Genomes will be added the key `fitness`)
genomes: [],
// // Current state of learning [STOP, LEARNING]
// Current genome tryout state: 'STOP',
//
genome: 0,
generation: 0,
// // Current genome/generation tryout
// Set this, to verify genome action BEFORE running it genome: 0,
// generation: 0,
shouldCheckExperience: false,
// Set this, to verify genome experience BEFORE running it
shouldCheckExperience: false,
}; };
//
// Initialize the Learner // Initialize the Learner
// Learn.init = function (gameManip, ui, genomeUnits, selection, mutationProb) {
Learn.init = function (gameManipulator, ui, genomeUnits, selection, mutationProb) { Learn.gm = gameManip;
Learn.gm = gameManipulator; Learn.ui = ui;
Learn.ui = ui;
Learn.genome = 0; Learn.genome = 0;
Learn.generation = 0; Learn.generation = 0;
Learn.genomeUnits = genomeUnits; Learn.genomeUnits = genomeUnits;
Learn.selection = selection; Learn.selection = selection;
Learn.mutationProb = mutationProb; Learn.mutationProb = mutationProb;
} }
//
// Build genomes before calling executeGeneration. // Build genomes before calling executeGeneration.
//
Learn.startLearning = function () { Learn.startLearning = function () {
// Build genomes if needed // Build genomes if needed
while(Learn.genomes.length < Learn.genomeUnits){ while (Learn.genomes.length < Learn.genomeUnits) {
Learn.genomes.push(Learn.buildGenome(3, 1)); Learn.genomes.push(Learn.buildGenome(3, 1));
} }
Learn.executeGeneration(); Learn.executeGeneration();
} }
//
// Given the entire generation of genomes (An array), // Given the entire generation of genomes (An array),
// applyes method `executeGenome` for each element. // applyes method `executeGenome` for each element.
// After all elements have completed executing: // After all elements have completed executing:
@@ -75,223 +60,212 @@ Learn.startLearning = function () {
// 2) Does cross over (except for 2 genomes) // 2) Does cross over (except for 2 genomes)
// 3) Does Mutation-only on remaining genomes // 3) Does Mutation-only on remaining genomes
// 4) Execute generation (recursivelly) // 4) Execute generation (recursivelly)
//
Learn.executeGeneration = function (){ Learn.executeGeneration = function (){
if(Learn.state == 'STOP') if (Learn.state == 'STOP') {
return; return;
}
Learn.generation++; Learn.generation++;
Learn.ui.logger.log('Executing generation '+Learn.generation); Learn.ui.logger.log('Executing generation '+Learn.generation);
Learn.genome = 0; Learn.genome = 0;
async.mapSeries(Learn.genomes, Learn.executeGenome, function (argument) { async.mapSeries(Learn.genomes, Learn.executeGenome, function (argument) {
// Kill worst genomes // Kill worst genomes
Learn.genomes = Learn.selectBestGenomes(Learn.selection); Learn.genomes = Learn.selectBestGenomes(Learn.selection);
// Copy best genomes // Copy best genomes
var bestGenomes = _.clone(Learn.genomes); var bestGenomes = _.clone(Learn.genomes);
// Cross Over () // Cross Over ()
while(Learn.genomes.length < Learn.genomeUnits - 2){ while (Learn.genomes.length < Learn.genomeUnits - 2) {
// Get two random Genomes // Get two random Genomes
var genA = _.sample(bestGenomes).toJSON(); var genA = _.sample(bestGenomes).toJSON();
var genB = _.sample(bestGenomes).toJSON(); var genB = _.sample(bestGenomes).toJSON();
// Cross over and Mutate // Cross over and Mutate
var newGenome = Learn.mutate(Learn.crossOver(genA, genB)); var newGenome = Learn.mutate(Learn.crossOver(genA, genB));
// Add to generation // Add to generation
Learn.genomes.push(Network.fromJSON(newGenome)); Learn.genomes.push(Network.fromJSON(newGenome));
} }
// Mutation-only // Mutation-only
while(Learn.genomes.length < Learn.genomeUnits){ while (Learn.genomes.length < Learn.genomeUnits) {
// Get two random Genomes // Get two random Genomes
var gen = _.sample(bestGenomes).toJSON(); var gen = _.sample(bestGenomes).toJSON();
// Cross over and Mutate // Cross over and Mutate
var newGenome = Learn.mutate(gen); var newGenome = Learn.mutate(gen);
// Add to generation // Add to generation
Learn.genomes.push(Network.fromJSON(newGenome)); Learn.genomes.push(Network.fromJSON(newGenome));
} }
Learn.ui.logger.log('Completed generation '+Learn.generation); Learn.ui.logger.log('Completed generation '+Learn.generation);
// Execute next generation // Execute next generation
Learn.executeGeneration(); Learn.executeGeneration();
}) })
} }
//
// Sort all the genomes, and delete the worst one // Sort all the genomes, and delete the worst one
// untill the genome list has selectN elements. // untill the genome list has selectN elements.
//
Learn.selectBestGenomes = function (selectN){ Learn.selectBestGenomes = function (selectN){
var selected = _.sortBy(Learn.genomes, 'fitness').reverse(); var selected = _.sortBy(Learn.genomes, 'fitness').reverse();
while(selected.length > selectN){ while (selected.length > selectN) {
selected.pop(); selected.pop();
} }
Learn.ui.logger.log('Fitness: '+_.pluck(selected, 'fitness').join(',')); Learn.ui.logger.log('Fitness: '+_.pluck(selected, 'fitness').join(','));
return selected; return selected;
} }
//
// Waits the game to end, and start a new one, then: // Waits the game to end, and start a new one, then:
// 1) Set's listener for sensorData // 1) Set's listener for sensorData
// 2) On data read, applyes the neural network, and // 2) On data read, applyes the neural network, and
// set it's output // set it's output
// 3) When the game has ended and compute the fitness // 3) When the game has ended and compute the fitness
//
Learn.executeGenome = function (genome, next){ Learn.executeGenome = function (genome, next){
if(Learn.state == 'STOP') if (Learn.state == 'STOP') {
return; return;
}
Learn.genome = Learn.genomes.indexOf(genome) + 1; Learn.genome = Learn.genomes.indexOf(genome) + 1;
// Learn.ui.logger.log('Executing genome '+Learn.genome); // Learn.ui.logger.log('Executing genome '+Learn.genome);
// Check if genome has AT LEAST some experience // Check if genome has AT LEAST some experience
if(Learn.shouldCheckExperience){ if (Learn.shouldCheckExperience) {
if(!Learn.checkExperience(genome)){ if (!Learn.checkExperience(genome)) {
genome.fitness = 0; genome.fitness = 0;
// Learn.ui.logger.log('Genome '+Learn.genome+' has no minimum experience'); // Learn.ui.logger.log('Genome '+Learn.genome+' has no min. experience');
return next(); return next();
} }
} }
Learn.gm.startNewGame(function (){ Learn.gm.startNewGame(function (){
// Reads sensor data, and apply network // Reads sensor data, and apply network
Learn.gm.onSensorData = function (){ Learn.gm.onSensorData = function (){
var inputs = [ var inputs = [
Learn.gm.sensors[0].value, Learn.gm.sensors[0].value,
Learn.gm.sensors[0].size, Learn.gm.sensors[0].size,
Learn.gm.sensors[0].speed, Learn.gm.sensors[0].speed,
]; ];
// console.log(inputs); // console.log(inputs);
// Apply to network // Apply to network
var outputs = genome.activate(inputs); var outputs = genome.activate(inputs);
Learn.gm.setGameOutput(outputs[0]); Learn.gm.setGameOutput(outputs[0]);
} }
// Wait game end, and compute fitness // Wait game end, and compute fitness
Learn.gm.onGameEnd = function (points){ Learn.gm.onGameEnd = function (points){
Learn.ui.logger.log('Genome '+Learn.genome+' ended. Fitness: '+points); Learn.ui.logger.log('Genome '+Learn.genome+' ended. Fitness: '+points);
// Save Genome fitness // Save Genome fitness
genome.fitness = points; genome.fitness = points;
// Go to next genome // Go to next genome
next(); next();
} }
}); });
} }
//
// Validate if any acction occur uppon a given input (in this case, distance). // Validate if any acction occur uppon a given input (in this case, distance).
// If genome only keeps a single activation value for any given input, // If genome only keeps a single activation value for any given input,
// it will return false // it will return false
//
Learn.checkExperience = function (genome) { Learn.checkExperience = function (genome) {
var step = 0.1, start = 0.0, stop = 1; var step = 0.1, start = 0.0, stop = 1;
// Inputs are default. We only want to test the first index // Inputs are default. We only want to test the first index
var inputs = [0.0, 0.3, 0.2]; var inputs = [0.0, 0.3, 0.2];
var activation, state, outputs = {}; var activation, state, outputs = {};
for(var k = start; k < stop; k += step){ for (var k = start; k < stop; k += step) {
inputs[0] = k; inputs[0] = k;
activation = genome.activate(inputs); activation = genome.activate(inputs);
state = Learn.gm.getDiscreteState(activation); state = Learn.gm.getDiscreteState(activation);
outputs[state] = true; outputs[state] = true;
} }
// Count states, and return true if greater than 1 // Count states, and return true if greater than 1
return _.keys(outputs).length > 1; return _.keys(outputs).length > 1;
} }
//
// Load genomes saved from JSON file // Load genomes saved from JSON file
//
Learn.loadGenomes = function (genomes, deleteOthers){ Learn.loadGenomes = function (genomes, deleteOthers){
if(deleteOthers) if (deleteOthers) {
Learn.genomes = []; Learn.genomes = [];
}
var loaded = 0; var loaded = 0;
for(var k in genomes){ for (var k in genomes) {
Learn.genomes.push(Network.fromJSON(genomes[k])); Learn.genomes.push(Network.fromJSON(genomes[k]));
loaded++; loaded++;
} }
Learn.ui.logger.log('Loaded '+loaded+' genomes!'); Learn.ui.logger.log('Loaded '+loaded+' genomes!');
} }
//
// Builds a new genome based on the // Builds a new genome based on the
// expected number of inputs and outputs // expected number of inputs and outputs
//
Learn.buildGenome = function (inputs, outputs) { Learn.buildGenome = function (inputs, outputs) {
Learn.ui.logger.log('Build genome '+(Learn.genomes.length+1)); Learn.ui.logger.log('Build genome '+(Learn.genomes.length+1));
var network = new Architect.Perceptron(inputs, 4, 4, outputs);
return network; var network = new Architect.Perceptron(inputs, 4, 4, outputs);
// console.log(JSON.stringify(network.toJSON()));
// process.exit(); return network;
} }
//
// SPECIFIC to Neural Network. // SPECIFIC to Neural Network.
// Those two methods convert from JSON to Array, and from Array to JSON // Those two methods convert from JSON to Array, and from Array to JSON
//
Learn.crossOver = function (netA, netB) { Learn.crossOver = function (netA, netB) {
// Swap (50% prob.) // Swap (50% prob.)
if(Math.random() > 0.5){ if (Math.random() > 0.5) {
var tmp = netA; var tmp = netA;
netA = netB; netA = netB;
netB = tmp; netB = tmp;
} }
// Clone network // Clone network
netA = _.cloneDeep(netA); netA = _.cloneDeep(netA);
netB = _.cloneDeep(netB); netB = _.cloneDeep(netB);
// Cross over data keys // Cross over data keys
Learn.crossOverDataKey(netA.neurons, netB.neurons, 'bias'); Learn.crossOverDataKey(netA.neurons, netB.neurons, 'bias');
// Learn.crossOverDataKey(netA.connections, netB.connections, 'weight');
return netA; return netA;
} }
//
// Does random mutations across all // Does random mutations across all
// the biases and weights of the Networks // the biases and weights of the Networks
// (This must be done in the JSON to // (This must be done in the JSON to
// prevent modifying the current one) // prevent modifying the current one)
//
Learn.mutate = function (net){ Learn.mutate = function (net){
// Mutate // Mutate
Learn.mutateDataKeys(net.neurons, 'bias', Learn.mutationProb); Learn.mutateDataKeys(net.neurons, 'bias', Learn.mutationProb);
Learn.mutateDataKeys(net.connections, 'weight', Learn.mutationProb); Learn.mutateDataKeys(net.connections, 'weight', Learn.mutationProb);
return net; return net;
} }
//
// Given an Object A and an object B, both Arrays // Given an Object A and an object B, both Arrays
// of Objects: // of Objects:
// //
@@ -299,33 +273,33 @@ Learn.mutate = function (net){
// randomly (going from 0 to A.length) // randomly (going from 0 to A.length)
// 2) Swap values from `key` one to another, // 2) Swap values from `key` one to another,
// starting by cutLocation // starting by cutLocation
//
Learn.crossOverDataKey = function (a, b, key) { Learn.crossOverDataKey = function (a, b, key) {
var cutLocation = Math.round(a.length * Math.random()); var cutLocation = Math.round(a.length * Math.random());
var tmp; var tmp;
for(var k = cutLocation; k < a.length; k++){ for (var k = cutLocation; k < a.length; k++) {
// Swap // Swap
tmp = a[k][key]; tmp = a[k][key];
a[k][key] = b[k][key]; a[k][key] = b[k][key];
b[k][key] = tmp; b[k][key] = tmp;
} }
} }
//
// Given an Array of objects with key `key`, // Given an Array of objects with key `key`,
// and also a `mutationRate`, randomly Mutate // and also a `mutationRate`, randomly Mutate
// the value of each key, if random value is // the value of each key, if random value is
// lower than mutationRate for each element. // lower than mutationRate for each element.
//
Learn.mutateDataKeys = function (a, key, mutationRate){ Learn.mutateDataKeys = function (a, key, mutationRate){
for(var k = 0; k < a.length; k++){ for (var k = 0; k < a.length; k++) {
// Should mutate? // Should mutate?
if(Math.random() > mutationRate) if (Math.random() > mutationRate) {
continue; continue;
}
a[k][key] += a[k][key] * (Math.random() - 0.5) * 3 + (Math.random() - 0.5); a[k][key] += a[k][key] * (Math.random() - 0.5) * 3 + (Math.random() - 0.5);
} }
} }
module.exports = Learn; module.exports = Learn;
+6 -1
Ver Arquivo
@@ -66,7 +66,7 @@ There are a few files in the project:
- `index.js`: It tight all things together. - `index.js`: It tight all things together.
- `scanner.js`: Basic abstraction layer above RobotJs library that reads the screen like - `Scanner.js`: Basic abstraction layer above RobotJs library that reads the screen like
ray tracing. Also have some utilities functions. ray tracing. Also have some utilities functions.
- `UI.js`: Global scope for the UI management. It initializes and also updates the screen - `UI.js`: Global scope for the UI management. It initializes and also updates the screen
@@ -103,6 +103,11 @@ console in the element inspector:
setInterval(function (){Runner.instance_.tRex.xPos = 21}, 2000) setInterval(function (){Runner.instance_.tRex.xPos = 21}, 2000)
``` ```
## Development guidelines
Please, follow the Node.js style guide from [Felix](https://github.com/felixge/node-style-guide).
It is not complex, and has a great simple pattern for things.
## Credits ## Credits
- [Ivan Seidel](https://github.com/ivanseidel) - [Ivan Seidel](https://github.com/ivanseidel)
+173 -185
Ver Arquivo
@@ -1,219 +1,207 @@
/*
Developed by Ivan Seidel [https://github.com/ivanseidel]
*/
var fs = require('fs');
var blessed = require('blessed')
var contrib = require('blessed-contrib') var contrib = require('blessed-contrib')
var blessed = require('blessed')
var fs = require('fs');
var screen = blessed.screen() var screen = blessed.screen()
var UI = {}; var UI = {};
//
// Initialize UI objects // Initialize UI objects
//
UI.init = function (gameManipulator, learner) { UI.init = function (gameManipulator, learner) {
UI.gm = gameManipulator; UI.gm = gameManipulator;
UI.learner = learner; UI.learner = learner;
UI.grid = new contrib.grid({ UI.grid = new contrib.grid({
rows: 12, rows: 12,
cols: 6, cols: 6,
screen: screen screen: screen
}); });
//
// Build Sensor inputs
//
UI.uiSensors = UI.grid.set(0, 0, 3, 6, contrib.bar, {
label: 'Network Inputs',
// bg: 'white',
barWidth: 12,
barSpacing: 1,
xOffset: 0,
maxHeight: 100,
});
//
// Build Log box
//
UI.logger = UI.grid.set(3, 0, 3, 6, contrib.log, {
fg: 'green',
selectedFg: 'green',
label: 'Logs'
});
//
// Current score/time view
//
UI.uiScore = UI.grid.set(6, 0, 3, 3, blessed.Text, {
label: 'Game Stats',
// bg: 'green',
fg: 'white',
content: 'Loading...',
align: 'center',
});
//
// Current Genomes stats
//
UI.uiGenomes = UI.grid.set(6, 3, 3, 3, blessed.Text, {
label: 'Genome Stats',
// bg: 'green',
fg: 'white',
content: 'Hey!',
align: 'center',
});
// // Build Sensor inputs
// Load Tree UI.uiSensors = UI.grid.set(0, 0, 3, 6, contrib.bar, {
// label: 'Network Inputs',
UI.savesTree = UI.grid.set(9, 0, 3, 3, contrib.tree, { // bg: 'white',
label: 'Saved Genomes', barWidth: 12,
}); barSpacing: 1,
xOffset: 0,
UI.savesTree.on('click', UI.savesTree.focus.bind(UI.savesTree)); maxHeight: 100,
UI.savesTree.on('select', function (item){ });
if(item.isFile){
var fileName = item.name;
UI.logger.log('Loading genomes from file:');
UI.logger.log(fileName);
var genomes = require('./genomes/'+fileName);
UI.learner.loadGenomes(genomes);
}else{
UI.refreshFiles();
}
});
UI.refreshFiles();
//
// Save Btn
//
UI.btnSave = UI.grid.set(9, 3, 3, 3, blessed.box, {
label: 'Save to File',
bg: 'green',
fg: 'red',
content: '\n\n\n\nSave Genomes',
align: 'center',
});
UI.btnSave.on('click', function (){
var jsonGenomes = [];
for(var k in UI.learner.genomes){
jsonGenomes.push(UI.learner.genomes[k].toJSON());
}
UI.logger.log('Saving '+jsonGenomes.length+' genomes...');
var fileName = './genomes/gen_'+UI.learner.generation+'_'+Date.now()+'.json';
fs.writeFile(fileName, JSON.stringify(jsonGenomes), function (err){
if(err)
UI.logger.log('Failed to save! '+err);
else
UI.logger.log('Saved to '+fileName);
UI.refreshFiles();
});
});
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
screen.key(['s'], function (ch, key){
if(learner.state == 'STOP'){
learner.state = 'LEARNING';
gameManipulator.focusGame();
learner.startLearning();
}else{
learner.state = 'STOP';
}
});
screen.render() // Build Log box
UI.logger = UI.grid.set(3, 0, 3, 6, contrib.log, {
fg: 'green',
selectedFg: 'green',
label: 'Logs'
});
// Current score/time view
UI.uiScore = UI.grid.set(6, 0, 3, 3, blessed.Text, {
label: 'Game Stats',
// bg: 'green',
fg: 'white',
content: 'Loading...',
align: 'center',
});
// Current Genomes stats
UI.uiGenomes = UI.grid.set(6, 3, 3, 3, blessed.Text, {
label: 'Genome Stats',
// bg: 'green',
fg: 'white',
content: 'Hey!',
align: 'center',
});
// Load Tree
UI.savesTree = UI.grid.set(9, 0, 3, 3, contrib.tree, {
label: 'Saved Genomes',
});
// Callback for Loading genomes and focusing tree
UI.savesTree.on('click', UI.savesTree.focus.bind(UI.savesTree));
UI.savesTree.on('select', function (item){
if (item.isFile) {
var fileName = item.name;
UI.logger.log('Loading genomes from file:');
UI.logger.log(fileName);
var genomes = require('./genomes/'+fileName);
UI.learner.loadGenomes(genomes);
} else {
UI.refreshFiles();
}
});
UI.refreshFiles();
// Save Btn
UI.btnSave = UI.grid.set(9, 3, 3, 3, blessed.box, {
label: 'Save to File',
bg: 'green',
fg: 'red',
content: '\n\n\n\nSave Genomes',
align: 'center',
});
UI.btnSave.on('click', function (){
var jsonGenomes = [];
for (var k in UI.learner.genomes) {
jsonGenomes.push(UI.learner.genomes[k].toJSON());
}
UI.logger.log('Saving '+jsonGenomes.length+' genomes...');
var dir = './genomes';
var fileName = dir + '/gen_'+UI.learner.generation+'_'+Date.now()+'.json';
fs.writeFile(fileName, JSON.stringify(jsonGenomes), function (err){
if (err) {
UI.logger.log('Failed to save! '+err);
} else {
UI.logger.log('Saved to '+fileName);
}
UI.refreshFiles();
});
});
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
screen.key(['s'], function (ch, key){
if (learner.state == 'STOP') {
learner.state = 'LEARNING';
gameManipulator.focusGame();
learner.startLearning();
} else {
learner.state = 'STOP';
}
});
screen.render()
}; };
//
// Read entire folder and select files that match a .json file // Read entire folder and select files that match a .json file
//
UI.refreshFiles = function (){ UI.refreshFiles = function (){
var files = []; var files = [];
var fileData = var fileData = {
{ name: 'Saved Files',
name: 'Saved Files', extended: true,
extended: true, children: [{
children: [{ name: 'Refresh Folders'
name: 'Refresh Folders' }]
}] };
};
// Populate tree // Populate tree
UI.logger.log('Reading genomes dir...') UI.logger.log('Reading genomes dir...')
var files = fs.readdirSync('./genomes'); var files = fs.readdirSync('./genomes');
for(var k in files){ for (var k in files) {
if(files[k].indexOf('.json') >= 0){ if (files[k].indexOf('.json') >= 0) {
fileData.children.push({
name: files[k], fileData.children.push({
isFile: true, name: files[k],
}); isFile: true,
} });
}
UI.savesTree.setData(fileData); }
}
UI.savesTree.setData(fileData);
} }
//
// Updates data on the screen and render it // Updates data on the screen and render it
//
UI.render = function () { UI.render = function () {
// // Update data
// Update data UI.uiSensors.setData({
// titles: ['Distance', 'Size', 'Speed', 'Activation'],
UI.uiSensors.setData({ data: [
titles: ['Distance', 'Size', 'Speed', 'Activation'], Math.round(UI.gm.sensors[0].value * 100),
data: [ Math.round(UI.gm.sensors[0].size * 100),
Math.round(UI.gm.sensors[0].value * 100), Math.round(UI.gm.sensors[0].speed * 100),
Math.round(UI.gm.sensors[0].size * 100), Math.round(UI.gm.gameOutput * 100),
Math.round(UI.gm.sensors[0].speed * 100), ]
Math.round(UI.gm.gameOutput * 100), })
]
})
// // Set Genome stats and score
// Set Genome stats and score var learn = UI.learner;
// var uiStats = '';
var learn = UI.learner; uiStats += 'Status: ' + learn.state + '\n';
var uiStats = 'Status: '+learn.state+'\n'; uiStats += 'Fitness: ' + UI.gm.points + '\n';
uiStats += 'Fitness: '+UI.gm.points+'\nGameStatus: '+UI.gm.gamestate + '\n'; uiStats += 'GameStatus: ' + UI.gm.gamestate + '\n';
uiStats += 'Generation: '+learn.generation+' : '+learn.genome+'/'+learn.genomes.length; uiStats += 'Generation: ' + learn.generation;
UI.uiScore.setText(uiStats); uiStats += ' : ' + learn.genome + '/' + learn.genomes.length;
UI.uiScore.setText(uiStats);
if(UI.gm.gameOutput){ if (UI.gm.gameOutput) {
var str = 'Action: '+UI.gm.gameOutputString+'\nActivation: '+UI.gm.gameOutput; var str = '';
UI.uiGenomes.setText(str); str += 'Action: ' + UI.gm.gameOutputString + '\n'
}else{ str += 'Activation: ' + UI.gm.gameOutput;
UI.uiGenomes.setText('Loading...'); UI.uiGenomes.setText(str);
} } else {
UI.uiGenomes.setText('Loading...');
}
// // Render screen
// Render screen screen.render();
//
screen.render();
} }
// Continuously render screen
setInterval(UI.render, 25); setInterval(UI.render, 25);
module.exports = UI; module.exports = UI;
+25 -40
Ver Arquivo
@@ -1,76 +1,61 @@
/*
Developed by Ivan Seidel [https://github.com/ivanseidel]
*/
var UI = require('./UI');
var robot = require('robotjs'); var robot = require('robotjs');
var Learner = require('./Learner');
var scanner = require('./scanner');
var GameManipulator = require('./GameManipulator');
// var GameManipulator = require('./GameManipulator');
var Learner = require('./Learner');
var Scanner = require('./Scanner');
var UI = require('./UI');
// Configure Robotjs // Configure Robotjs
//
robot.setMouseDelay(1); robot.setMouseDelay(1);
//
// Initialize Game // Initialize Game
//
GameManipulator.findGamePosition(); GameManipulator.findGamePosition();
//
// Check for found game // Check for found game
// if (GameManipulator.offset) {
if(GameManipulator.offset){ // Uncomment this line to debug the
// Uncomment this line to debug the // starting point of sensor (Check if it's detecting it correcly)
// starting point (Check if it's detecting it correcly)
// robot.moveMouse(GameManipulator.offset[0]+GameManipulator.sensors[0].offset[0], // robot.moveMouse(GameManipulator.offset[0]+GameManipulator.sensors[0].offset[0],
// GameManipulator.offset[1] + GameManipulator.sensors[0].offset[1]); // GameManipulator.offset[1] + GameManipulator.sensors[0].offset[1]);
robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]); robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]);
}else{ } else {
console.error('FAILED TO FIND GAME!'); console.error('FAILED TO FIND GAME!');
process.exit(); process.exit();
} }
//
// Initialize UI // Initialize UI
//
UI.init(GameManipulator, Learner); UI.init(GameManipulator, Learner);
//
// Init Learner // Init Learner
//
Learner.init(GameManipulator, UI, 12, 4, 0.2); Learner.init(GameManipulator, UI, 12, 4, 0.2);
//
// Start reading game state and sensors // Start reading game state and sensors
//
setInterval(GameManipulator.readSensors, 40); setInterval(GameManipulator.readSensors, 40);
setInterval(GameManipulator.readGameState, 200); setInterval(GameManipulator.readGameState, 200);
//
// Start game (Example of API usage) // Start game (Example of API usage)
//
/* /*
function startGame () { function startGame () {
var game = Math.round(Math.random() * 100); var game = Math.round(Math.random() * 100);
UI.logger.log('Queuing start... ', game); UI.logger.log('Queuing start... ', game);
GameManipulator.startNewGame(function() { GameManipulator.startNewGame(function() {
UI.logger.log('Game HAS started!', game); UI.logger.log('Game HAS started!', game);
GameManipulator.onGameEnd = function () { GameManipulator.onGameEnd = function () {
UI.logger.log('Game HAS ended!', game); UI.logger.log('Game HAS ended!', game);
startGame(); startGame();
} }
}); });
} }
*/ */
+69 -63
Ver Arquivo
@@ -1,97 +1,103 @@
/*
Developed by Ivan Seidel [https://github.com/ivanseidel]
*/
// External Modules
var robot = require('robotjs'); var robot = require('robotjs');
// Cache screen size
var screenSize = robot.getScreenSize(); var screenSize = robot.getScreenSize();
// Indexes // Indexes
var X = 0; var X = 0;
var Y = 1; var Y = 1;
// Create the "class" wrapper // Create the "class" wrapper
var scanner = {}; var Scanner = {};
//
// Check if the given position is outside the Screen // Check if the given position is outside the Screen
// Scanner.isOutOfBound = function (pos) {
scanner.isOutOfBound = function (pos) { if ( pos[X] < 0 || pos[Y] < 0 ||
if( pos[X] < 0 || pos[Y] < 0 || pos[X] >= screenSize.width ||
pos[X] >= screenSize.width || pos[Y] >= screenSize.height) {
pos[Y] >= screenSize.height)
return true;
return false; return true;
}
return false;
} }
//
// Limits the x/y values of position to fit the screen // Limits the x/y values of position to fit the screen
// Scanner.makeInBounds = function (pos) {
scanner.makeInBounds = function (pos) {
if(pos[X] < 0) if (pos[X] < 0) {
pos[X] = 0; pos[X] = 0;
}
if(pos[X] >= screenSize.width) if (pos[X] >= screenSize.width) {
pos[X] = screenSize.width - 1; pos[X] = screenSize.width - 1;
}
if(pos[Y] < 0) if (pos[Y] < 0) {
pos[Y] = 0; pos[Y] = 0;
}
if(pos[Y] >= screenSize.height) if (pos[Y] >= screenSize.height) {
pos[Y] = screenSize.height - 1; pos[Y] = screenSize.height - 1;
}
return pos; return pos;
} }
/*
Given start [X, Y], and a DELTA [dX, dY],
maps from "start", adding "delta" to position,
until "matchinColor" is found OR isOutOfBounds.
If iterations reach > iterLimit: // Given start [X, Y], and a DELTA [dX, dY],
returns null; // maps from "start", adding "delta" to position,
// until "matchinColor" is found OR isOutOfBounds.
//
// If iterations reach > iterLimit:
// returns null;
//
// if isOutOfBounds:
// returns null
//
// otherwise:
// return that point
//
// Example: (X direction)
// scanUntil([0,0], [1, 0], "000000");
Scanner.scanUntil = function (start, delta, matchColor, inverted, iterLimit) {
var color, current, iterations = 0;
if isOutOfBounds: // (CLONE instead of using the real one)
returns null current = Scanner.makeInBounds([start[X], start[Y]]);
otherwise: if (delta[X] == 0 && delta[Y] == 0) {
return that point return null;
}
Example: (X direction)
scanUntil([0,0], [1, 0], "000000");
*/
scanner.scanUntil = function (start, delta, matchingColor, invertMatch, iterLimit) {
var color, current, iterations = 0;
// (CLONE instead of using the real one)
current = scanner.makeInBounds([start[X], start[Y]]);
if(delta[X] == 0 && delta[Y] == 0)
return null;
while(!scanner.isOutOfBound(current)){ while (!Scanner.isOutOfBound(current)) {
// Check current pixel // Check current pixel
color = robot.getPixelColor(current[X], current[Y]); color = robot.getPixelColor(current[X], current[Y]);
if(!invertMatch && color.toString() == matchingColor) if (!inverted && color.toString() == matchColor) {
return current; return current;
}
if(invertMatch && color.toString() != matchingColor) if (inverted && color.toString() != matchColor) {
return current; return current;
}
current[X] += delta[X]; current[X] += delta[X];
current[Y] += delta[Y]; current[Y] += delta[Y];
iterations++; iterations++;
if(iterations > iterLimit) if (iterations > iterLimit) {
return null; return null;
} }
}
return null; return null;
}; };
// Export the module // Export the module
module.exports = scanner; module.exports = Scanner;