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:
+282
-306
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||||
Referência em uma Nova Issue
Bloquear um usuário