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 scanner = require ('./scanner');
|
||||
// Cache screen size
|
||||
var screenSize = robot.getScreenSize();
|
||||
|
||||
//
|
||||
// Color definitions
|
||||
//
|
||||
var Scanner = require ('./Scanner');
|
||||
|
||||
// COLOR DEFINITIONS
|
||||
// This is the Dino's colour, also used by Obstacles.
|
||||
var COLOR_DINOSAUR = "535353";
|
||||
var COLOR_DINOSAUR = '535353';
|
||||
|
||||
|
||||
var GameManipulator = {
|
||||
|
||||
// Stores the game position (Globally)
|
||||
offset: null,
|
||||
width: null,
|
||||
// Stores the game position (Globally)
|
||||
offset: null,
|
||||
width: null,
|
||||
|
||||
// Stores points (jumps)
|
||||
points: 0,
|
||||
// Stores points (jumps)
|
||||
points: 0,
|
||||
|
||||
// Listners
|
||||
onGameEnd: null,
|
||||
onGameStart: null,
|
||||
onSensorData: null,
|
||||
// Listners
|
||||
onGameEnd: null,
|
||||
onGameStart: null,
|
||||
onSensorData: null,
|
||||
|
||||
// Game State
|
||||
gamestate: 'OVER',
|
||||
// Game State
|
||||
gamestate: 'OVER',
|
||||
|
||||
// GameOver Position
|
||||
gameOverOffset: [190, -82],
|
||||
// GameOver Position
|
||||
gameOverOffset: [190, -82],
|
||||
|
||||
//
|
||||
// Stores an array of "sensors" (Ray tracings)
|
||||
// Positions are always relative to global "offset"
|
||||
//
|
||||
sensors: [
|
||||
{
|
||||
lastValue: 1,
|
||||
// Stores an array of "sensors" (Ray tracings)
|
||||
// Positions are always relative to global "offset"
|
||||
sensors: [
|
||||
{
|
||||
lastValue: 1,
|
||||
|
||||
value: null,
|
||||
offset: [84, -15], // 64,-15
|
||||
step: [4, 0],
|
||||
length: 0.3,
|
||||
value: null,
|
||||
offset: [84, -15], // 64,-15
|
||||
step: [4, 0],
|
||||
length: 0.3,
|
||||
|
||||
// Speed
|
||||
speed: 0,
|
||||
lastComputeSpeed: 0,
|
||||
// Speed
|
||||
speed: 0,
|
||||
lastComputeSpeed: 0,
|
||||
|
||||
// Computes size of the object
|
||||
size: 0,
|
||||
computeSize: true,
|
||||
},
|
||||
|
||||
// {
|
||||
// lastValue: 1,
|
||||
|
||||
// value: null,
|
||||
// offset: [66, -30],
|
||||
// step: [5, 0],
|
||||
// length: 0.5,
|
||||
// }
|
||||
]
|
||||
// Computes size of the object
|
||||
size: 0,
|
||||
computeSize: true,
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Find out dinosaur (fast)
|
||||
//
|
||||
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(
|
||||
// Start position
|
||||
[x, 80],
|
||||
// Skip pixels
|
||||
[0, skipXFast],
|
||||
// Searching Color
|
||||
COLOR_DINOSAUR,
|
||||
// Normal mode (not inverse)
|
||||
false,
|
||||
// Iteration limit
|
||||
500 / skipXFast);
|
||||
if (dinoPos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dinoPos)
|
||||
break;
|
||||
}
|
||||
if (!dinoPos) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!dinoPos)
|
||||
return null;
|
||||
for (var x = dinoPos[0] - 50; x <= dinoPos[0]; x += 1) {
|
||||
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(
|
||||
// Start position
|
||||
[x, dinoPos[1] - 2],
|
||||
// Skip pixels
|
||||
[0, 1],
|
||||
// Searching Color
|
||||
COLOR_DINOSAUR,
|
||||
// Normal mode (not inverse)
|
||||
false,
|
||||
// Iteration limit
|
||||
100);
|
||||
// Did actually found? If not, error!
|
||||
if (!pos) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(pos)
|
||||
break;
|
||||
}
|
||||
// Find the end of the game
|
||||
var endPos = pos;
|
||||
|
||||
// Did actually found? If not, error!
|
||||
if(!pos)
|
||||
return null;
|
||||
while (robot.getPixelColor(endPos[0] + 3, endPos[1]) == COLOR_DINOSAUR) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Find the end of the game
|
||||
var endPos = pos;
|
||||
// Save to allow global access
|
||||
GameManipulator.offset = pos;
|
||||
GameManipulator.width = 600;//endPos[0] - pos[0];
|
||||
|
||||
while(robot.getPixelColor(endPos[0] + 3, endPos[1]) == COLOR_DINOSAUR){
|
||||
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;
|
||||
return pos;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Read Game state
|
||||
// (If game is ended or is playing)
|
||||
//
|
||||
GameManipulator.readGameState = function () {
|
||||
// Read GameOver
|
||||
var found = scanner.scanUntil(
|
||||
[
|
||||
GameManipulator.offset[0] + GameManipulator.gameOverOffset[0],
|
||||
GameManipulator.offset[1] + GameManipulator.gameOverOffset[1]
|
||||
],
|
||||
// Read GameOver
|
||||
var found = Scanner.scanUntil(
|
||||
[
|
||||
GameManipulator.offset[0] + GameManipulator.gameOverOffset[0],
|
||||
GameManipulator.offset[1] + GameManipulator.gameOverOffset[1]
|
||||
],
|
||||
|
||||
[2, 0], COLOR_DINOSAUR, false, 20);
|
||||
[2, 0], COLOR_DINOSAUR, false, 20);
|
||||
|
||||
if(found && GameManipulator.gamestate != 'OVER'){
|
||||
GameManipulator.gamestate = 'OVER';
|
||||
if (found && GameManipulator.gamestate != 'OVER') {
|
||||
GameManipulator.gamestate = 'OVER';
|
||||
|
||||
// Clear keys
|
||||
GameManipulator.setGameOutput(0.5);
|
||||
// Clear keys
|
||||
GameManipulator.setGameOutput(0.5);
|
||||
|
||||
// Trigger callback and clear
|
||||
GameManipulator.onGameEnd && GameManipulator.onGameEnd(GameManipulator.points);
|
||||
GameManipulator.onGameEnd = null;
|
||||
// Trigger callback and clear
|
||||
GameManipulator.onGameEnd && GameManipulator.onGameEnd(GameManipulator.points);
|
||||
GameManipulator.onGameEnd = null;
|
||||
|
||||
// console.log('GAME OVER: '+GameManipulator.points);
|
||||
// console.log('GAME OVER: '+GameManipulator.points);
|
||||
|
||||
}else if(!found && GameManipulator.gamestate != 'PLAYING'){
|
||||
GameManipulator.gamestate = 'PLAYING';
|
||||
} else if (!found && GameManipulator.gamestate != 'PLAYING') {
|
||||
GameManipulator.gamestate = 'PLAYING';
|
||||
|
||||
// Clear points
|
||||
GameManipulator.points = 0;
|
||||
GameManipulator.lastScore = 0;
|
||||
// Clear points
|
||||
GameManipulator.points = 0;
|
||||
GameManipulator.lastScore = 0;
|
||||
|
||||
// Clear keys
|
||||
GameManipulator.setGameOutput(0.5);
|
||||
// Clear keys
|
||||
GameManipulator.setGameOutput(0.5);
|
||||
|
||||
// Clear sensors
|
||||
GameManipulator.sensors[0].lastComputeSpeed = 0;
|
||||
GameManipulator.sensors[0].lastSpeeds = [];
|
||||
GameManipulator.sensors[0].lastValue = 1;
|
||||
GameManipulator.sensors[0].value = 1;
|
||||
GameManipulator.sensors[0].speed = 0;
|
||||
GameManipulator.sensors[0].size = 0;
|
||||
// Clear sensors
|
||||
GameManipulator.sensors[0].lastComputeSpeed = 0;
|
||||
GameManipulator.sensors[0].lastSpeeds = [];
|
||||
GameManipulator.sensors[0].lastValue = 1;
|
||||
GameManipulator.sensors[0].value = 1;
|
||||
GameManipulator.sensors[0].speed = 0;
|
||||
GameManipulator.sensors[0].size = 0;
|
||||
|
||||
// Clar Output flags
|
||||
GameManipulator.lastOutputSet = 'NONE';
|
||||
// Clar Output flags
|
||||
GameManipulator.lastOutputSet = 'NONE';
|
||||
|
||||
// Trigger callback and clear
|
||||
GameManipulator.onGameStart && GameManipulator.onGameStart();
|
||||
GameManipulator.onGameStart = null;
|
||||
// Trigger callback and clear
|
||||
GameManipulator.onGameStart && GameManipulator.onGameStart();
|
||||
GameManipulator.onGameStart = null;
|
||||
|
||||
// console.log('GAME RUNNING '+GameManipulator.points);
|
||||
}
|
||||
// console.log('GAME RUNNING '+GameManipulator.points);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Call this to start a fresh new game
|
||||
// Will wait untill game has ended,
|
||||
// and call the `next` callback
|
||||
//
|
||||
var _startKeyInterval;
|
||||
GameManipulator.startNewGame = function (next) {
|
||||
|
||||
// Refresh state
|
||||
GameManipulator.readGameState();
|
||||
// Refresh state
|
||||
GameManipulator.readGameState();
|
||||
|
||||
// If game is already over, press space
|
||||
if(GameManipulator.gamestate == 'OVER'){
|
||||
clearInterval(_startKeyInterval);
|
||||
// If game is already over, press space
|
||||
if (GameManipulator.gamestate == 'OVER') {
|
||||
clearInterval(_startKeyInterval);
|
||||
|
||||
// Set start callback
|
||||
GameManipulator.onGameStart = function (argument) {
|
||||
clearInterval(_startKeyInterval);
|
||||
next && next();
|
||||
};
|
||||
// Set start callback
|
||||
GameManipulator.onGameStart = function (argument) {
|
||||
clearInterval(_startKeyInterval);
|
||||
next && next();
|
||||
};
|
||||
|
||||
// Press space to begin game (repetidelly)
|
||||
_startKeyInterval = setInterval(function (){
|
||||
robot.keyTap(' ');
|
||||
}, 300);
|
||||
// Press space to begin game (repetidelly)
|
||||
_startKeyInterval = setInterval(function (){
|
||||
robot.keyTap(' ');
|
||||
}, 300);
|
||||
|
||||
// Refresh state
|
||||
GameManipulator.readGameState();
|
||||
// Refresh state
|
||||
GameManipulator.readGameState();
|
||||
|
||||
}else{
|
||||
// Wait die, and call recursive action
|
||||
GameManipulator.onGameEnd = function () {
|
||||
GameManipulator.startNewGame(next);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Wait die, and call recursive action
|
||||
GameManipulator.onGameEnd = function () {
|
||||
GameManipulator.startNewGame(next);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Compute points based on sensors
|
||||
//
|
||||
// Basicaly, checks if an object has
|
||||
// passed trough the sensor and the
|
||||
// value is now higher than before
|
||||
//
|
||||
GameManipulator.computePoints = function () {
|
||||
for(var k in GameManipulator.sensors){
|
||||
var sensor = GameManipulator.sensors[k];
|
||||
for (var k in GameManipulator.sensors) {
|
||||
var sensor = GameManipulator.sensors[k];
|
||||
|
||||
if(sensor.value > 0.5 && sensor.lastValue < 0.3){
|
||||
GameManipulator.points++;
|
||||
// console.log('POINTS: '+GameManipulator.points);
|
||||
}
|
||||
}
|
||||
if (sensor.value > 0.5 && sensor.lastValue < 0.3) {
|
||||
GameManipulator.points++;
|
||||
// console.log('POINTS: '+GameManipulator.points);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Read sensors
|
||||
//
|
||||
// Sensors are like ray-traces:
|
||||
@@ -274,183 +252,181 @@ GameManipulator.computePoints = function () {
|
||||
// SIZE and it's speed
|
||||
//
|
||||
// Note: We currently only have a sensor.
|
||||
//
|
||||
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
|
||||
var start = [
|
||||
offset[0] + sensor.offset[0],
|
||||
offset[1] + sensor.offset[1],
|
||||
];
|
||||
// Calculate absolute position of ray tracing
|
||||
var start = [
|
||||
offset[0] + sensor.offset[0],
|
||||
offset[1] + sensor.offset[1],
|
||||
];
|
||||
|
||||
// Compute cursor forwarding
|
||||
var forward = sensor.value * GameManipulator.width * 0.8 * sensor.length;
|
||||
// Compute cursor forwarding
|
||||
var forward = sensor.value * GameManipulator.width * 0.8 * sensor.length;
|
||||
|
||||
var end = scanner.scanUntil(
|
||||
// console.log(
|
||||
// Start position
|
||||
[start[0], start[1]],
|
||||
// Skip pixels
|
||||
sensor.step,
|
||||
// Searching Color
|
||||
COLOR_DINOSAUR,
|
||||
// Invert mode?
|
||||
false,
|
||||
// Iteration limit
|
||||
(GameManipulator.width * sensor.length) / sensor.step[0]);
|
||||
var end = Scanner.scanUntil(
|
||||
// console.log(
|
||||
// Start position
|
||||
[start[0], start[1]],
|
||||
// Skip pixels
|
||||
sensor.step,
|
||||
// Searching Color
|
||||
COLOR_DINOSAUR,
|
||||
// Invert mode?
|
||||
false,
|
||||
// Iteration limit
|
||||
(GameManipulator.width * sensor.length) / sensor.step[0]);
|
||||
|
||||
// Save lastValue
|
||||
sensor.lastValue = sensor.value;
|
||||
// Save lastValue
|
||||
sensor.lastValue = sensor.value;
|
||||
|
||||
// Calculate the Sensor value
|
||||
if(end){
|
||||
sensor.value = (end[0] - start[0]) / (GameManipulator.width * sensor.length);
|
||||
// Calculate the Sensor value
|
||||
if (end) {
|
||||
sensor.value = (end[0] - start[0]) / (GameManipulator.width * sensor.length);
|
||||
|
||||
//
|
||||
// Calculate size of obstacle
|
||||
//
|
||||
var endPoint = scanner.scanUntil(
|
||||
[end[0] + 75, end[1]],
|
||||
[-2, 0],
|
||||
COLOR_DINOSAUR,
|
||||
false,
|
||||
75 / 2
|
||||
);
|
||||
// Calculate size of obstacle
|
||||
var endPoint = Scanner.scanUntil(
|
||||
[end[0] + 75, end[1]],
|
||||
[-2, 0],
|
||||
COLOR_DINOSAUR,
|
||||
false,
|
||||
75 / 2
|
||||
);
|
||||
|
||||
// If no end point, set the start point as end
|
||||
if(!endPoint)
|
||||
endPoint = end;
|
||||
// If no end point, set the start point as end
|
||||
if (!endPoint) {
|
||||
endPoint = end;
|
||||
}
|
||||
|
||||
var sizeTmp = (endPoint[0] - end[0]) / 100.0;
|
||||
if(GameManipulator.points == sensor.lastScore){
|
||||
// It's the same obstacle. Set size to "max" of both
|
||||
sensor.size = Math.max(sensor.size, sizeTmp);
|
||||
}else{
|
||||
sensor.size = sizeTmp;
|
||||
}
|
||||
var sizeTmp = (endPoint[0] - end[0]) / 100.0;
|
||||
if (GameManipulator.points == sensor.lastScore) {
|
||||
// It's the same obstacle. Set size to "max" of both
|
||||
sensor.size = Math.max(sensor.size, sizeTmp);
|
||||
} else {
|
||||
sensor.size = sizeTmp;
|
||||
}
|
||||
|
||||
|
||||
// We use the current score to check for object equality
|
||||
sensor.lastScore = GameManipulator.points;
|
||||
// We use the current score to check for object equality
|
||||
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{
|
||||
sensor.value = 1;
|
||||
sensor.size = 0;
|
||||
}
|
||||
} else {
|
||||
sensor.value = 1;
|
||||
sensor.size = 0;
|
||||
}
|
||||
|
||||
// Compute speed
|
||||
var dt = (Date.now() - sensor.lastComputeSpeed) / 1000;
|
||||
sensor.lastComputeSpeed = Date.now();
|
||||
// Compute speed
|
||||
var dt = (Date.now() - sensor.lastComputeSpeed) / 1000;
|
||||
sensor.lastComputeSpeed = Date.now();
|
||||
|
||||
if(sensor.value < sensor.lastValue){
|
||||
// Compute speed
|
||||
var newSpeed = (sensor.lastValue - sensor.value) / dt;
|
||||
if (sensor.value < sensor.lastValue) {
|
||||
// Compute speed
|
||||
var newSpeed = (sensor.lastValue - sensor.value) / dt;
|
||||
|
||||
sensor.lastSpeeds.unshift(newSpeed);
|
||||
sensor.lastSpeeds.unshift(newSpeed);
|
||||
|
||||
while(sensor.lastSpeeds.length > 10)
|
||||
sensor.lastSpeeds.pop();
|
||||
while (sensor.lastSpeeds.length > 10) {
|
||||
sensor.lastSpeeds.pop();
|
||||
}
|
||||
|
||||
// Take Average
|
||||
var avgSpeed = 0;
|
||||
for(var k in sensor.lastSpeeds)
|
||||
avgSpeed += sensor.lastSpeeds[k] / sensor.lastSpeeds.length;
|
||||
// Take Average
|
||||
var avgSpeed = 0;
|
||||
for (var k in sensor.lastSpeeds) {
|
||||
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')
|
||||
// console.log("S["+k+"]: ", sensor.value, sensor.size, Date.now() - startTime);
|
||||
startTime = Date.now();
|
||||
}
|
||||
|
||||
startTime = Date.now();
|
||||
}
|
||||
// Compute points
|
||||
GameManipulator.computePoints();
|
||||
|
||||
// Compute points
|
||||
GameManipulator.computePoints();
|
||||
|
||||
// Call sensor callback (to act)
|
||||
GameManipulator.onSensorData && GameManipulator.onSensorData();
|
||||
// Call sensor callback (to act)
|
||||
GameManipulator.onSensorData && GameManipulator.onSensorData();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Set action to game
|
||||
// Values:
|
||||
// 0.0 to 0.4: DOWN
|
||||
// 0.4 to 0.6: NOTHING
|
||||
// 0.6 to 1.0: UP (JUMP)
|
||||
//
|
||||
// 0.00 to 0.45: DOWN
|
||||
// 0.45 to 0.55: NOTHING
|
||||
// 0.55 to 1.00: UP (JUMP)
|
||||
var PRESS = 'down';
|
||||
var RELEASE = 'up';
|
||||
|
||||
GameManipulator.lastOutputSet = 'NONE';
|
||||
GameManipulator.lastOutputSetTime = 0;
|
||||
|
||||
GameManipulator.setGameOutput = function (output){
|
||||
|
||||
GameManipulator.gameOutput = output;
|
||||
GameManipulator.gameOutputString = GameManipulator.getDiscreteState(output);
|
||||
GameManipulator.gameOutput = output;
|
||||
GameManipulator.gameOutputString = GameManipulator.getDiscreteState(output);
|
||||
|
||||
if(GameManipulator.gameOutputString == 'DOWN'){
|
||||
// Skew
|
||||
robot.keyToggle('up', RELEASE);
|
||||
robot.keyToggle('down', PRESS);
|
||||
}else if(GameManipulator.gameOutputString == 'NORM'){
|
||||
// DO Nothing
|
||||
robot.keyToggle('up', RELEASE);
|
||||
robot.keyToggle('down', RELEASE);
|
||||
}else{
|
||||
if (GameManipulator.gameOutputString == 'DOWN') {
|
||||
// Skew
|
||||
robot.keyToggle('up', RELEASE);
|
||||
robot.keyToggle('down', PRESS);
|
||||
} else if (GameManipulator.gameOutputString == 'NORM') {
|
||||
// DO Nothing
|
||||
robot.keyToggle('up', RELEASE);
|
||||
robot.keyToggle('down', RELEASE);
|
||||
} else {
|
||||
|
||||
// Filter JUMP
|
||||
if(GameManipulator.lastOutputSet != 'JUMP')
|
||||
GameManipulator.lastOutputSetTime = Date.now();
|
||||
// Filter JUMP
|
||||
if (GameManipulator.lastOutputSet != 'JUMP') {
|
||||
GameManipulator.lastOutputSetTime = Date.now();
|
||||
}
|
||||
|
||||
// JUMP
|
||||
// Check if hasn't jump for more than 3 continuous secconds
|
||||
if(Date.now() - GameManipulator.lastOutputSetTime < 3000){
|
||||
robot.keyToggle('up', PRESS);
|
||||
robot.keyToggle('down', RELEASE);
|
||||
}else{
|
||||
robot.keyToggle('up', RELEASE);
|
||||
robot.keyToggle('down', RELEASE);
|
||||
}
|
||||
// JUMP
|
||||
// Check if hasn't jump for more than 3 continuous secconds
|
||||
if (Date.now() - GameManipulator.lastOutputSetTime < 3000) {
|
||||
robot.keyToggle('up', PRESS);
|
||||
robot.keyToggle('down', RELEASE);
|
||||
} else {
|
||||
robot.keyToggle('up', RELEASE);
|
||||
robot.keyToggle('down', RELEASE);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
GameManipulator.lastOutputSet = GameManipulator.gameOutputString;
|
||||
GameManipulator.lastOutputSet = GameManipulator.gameOutputString;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Simply maps an real number to string actions
|
||||
//
|
||||
GameManipulator.getDiscreteState = function (value){
|
||||
if(value < 0.45){
|
||||
return 'DOWN'
|
||||
}else if(value > 0.55){
|
||||
return 'JUMP';
|
||||
}
|
||||
if (value < 0.45) {
|
||||
return 'DOWN'
|
||||
} else if(value > 0.55) {
|
||||
return 'JUMP';
|
||||
}
|
||||
|
||||
return 'NORM';
|
||||
return 'NORM';
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Click on the Starting point
|
||||
// to make sure game is focused
|
||||
//
|
||||
GameManipulator.focusGame = function (){
|
||||
robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]);
|
||||
robot.mouseClick("left");
|
||||
robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]);
|
||||
robot.mouseClick('left');
|
||||
}
|
||||
|
||||
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 async = require('async');
|
||||
var _ = require('lodash');
|
||||
|
||||
var Network = synaptic.Network;
|
||||
var Architect = synaptic.Architect;
|
||||
var Network = synaptic.Network;
|
||||
|
||||
|
||||
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 genome tryout
|
||||
//
|
||||
genome: 0,
|
||||
generation: 0,
|
||||
// Current state of learning [STOP, LEARNING]
|
||||
state: 'STOP',
|
||||
|
||||
//
|
||||
// Set this, to verify genome action BEFORE running it
|
||||
//
|
||||
shouldCheckExperience: false,
|
||||
// Current genome/generation tryout
|
||||
genome: 0,
|
||||
generation: 0,
|
||||
|
||||
// Set this, to verify genome experience BEFORE running it
|
||||
shouldCheckExperience: false,
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Initialize the Learner
|
||||
//
|
||||
Learn.init = function (gameManipulator, ui, genomeUnits, selection, mutationProb) {
|
||||
Learn.gm = gameManipulator;
|
||||
Learn.ui = ui;
|
||||
Learn.init = function (gameManip, ui, genomeUnits, selection, mutationProb) {
|
||||
Learn.gm = gameManip;
|
||||
Learn.ui = ui;
|
||||
|
||||
Learn.genome = 0;
|
||||
Learn.generation = 0;
|
||||
Learn.genome = 0;
|
||||
Learn.generation = 0;
|
||||
|
||||
Learn.genomeUnits = genomeUnits;
|
||||
Learn.selection = selection;
|
||||
Learn.mutationProb = mutationProb;
|
||||
Learn.genomeUnits = genomeUnits;
|
||||
Learn.selection = selection;
|
||||
Learn.mutationProb = mutationProb;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Build genomes before calling executeGeneration.
|
||||
//
|
||||
Learn.startLearning = function () {
|
||||
|
||||
// Build genomes if needed
|
||||
while(Learn.genomes.length < Learn.genomeUnits){
|
||||
Learn.genomes.push(Learn.buildGenome(3, 1));
|
||||
}
|
||||
// Build genomes if needed
|
||||
while (Learn.genomes.length < Learn.genomeUnits) {
|
||||
Learn.genomes.push(Learn.buildGenome(3, 1));
|
||||
}
|
||||
|
||||
Learn.executeGeneration();
|
||||
|
||||
Learn.executeGeneration();
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Given the entire generation of genomes (An array),
|
||||
// applyes method `executeGenome` for each element.
|
||||
// After all elements have completed executing:
|
||||
@@ -75,223 +60,212 @@ Learn.startLearning = function () {
|
||||
// 2) Does cross over (except for 2 genomes)
|
||||
// 3) Does Mutation-only on remaining genomes
|
||||
// 4) Execute generation (recursivelly)
|
||||
//
|
||||
Learn.executeGeneration = function (){
|
||||
if(Learn.state == 'STOP')
|
||||
return;
|
||||
if (Learn.state == 'STOP') {
|
||||
return;
|
||||
}
|
||||
|
||||
Learn.generation++;
|
||||
Learn.ui.logger.log('Executing generation '+Learn.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
|
||||
Learn.genomes = Learn.selectBestGenomes(Learn.selection);
|
||||
// Kill worst genomes
|
||||
Learn.genomes = Learn.selectBestGenomes(Learn.selection);
|
||||
|
||||
// Copy best genomes
|
||||
var bestGenomes = _.clone(Learn.genomes);
|
||||
// Copy best genomes
|
||||
var bestGenomes = _.clone(Learn.genomes);
|
||||
|
||||
// Cross Over ()
|
||||
while(Learn.genomes.length < Learn.genomeUnits - 2){
|
||||
// Get two random Genomes
|
||||
var genA = _.sample(bestGenomes).toJSON();
|
||||
var genB = _.sample(bestGenomes).toJSON();
|
||||
// Cross Over ()
|
||||
while (Learn.genomes.length < Learn.genomeUnits - 2) {
|
||||
// Get two random Genomes
|
||||
var genA = _.sample(bestGenomes).toJSON();
|
||||
var genB = _.sample(bestGenomes).toJSON();
|
||||
|
||||
// Cross over and Mutate
|
||||
var newGenome = Learn.mutate(Learn.crossOver(genA, genB));
|
||||
// Cross over and Mutate
|
||||
var newGenome = Learn.mutate(Learn.crossOver(genA, genB));
|
||||
|
||||
// Add to generation
|
||||
Learn.genomes.push(Network.fromJSON(newGenome));
|
||||
}
|
||||
// Add to generation
|
||||
Learn.genomes.push(Network.fromJSON(newGenome));
|
||||
}
|
||||
|
||||
// Mutation-only
|
||||
while(Learn.genomes.length < Learn.genomeUnits){
|
||||
// Get two random Genomes
|
||||
var gen = _.sample(bestGenomes).toJSON();
|
||||
// Mutation-only
|
||||
while (Learn.genomes.length < Learn.genomeUnits) {
|
||||
// Get two random Genomes
|
||||
var gen = _.sample(bestGenomes).toJSON();
|
||||
|
||||
// Cross over and Mutate
|
||||
var newGenome = Learn.mutate(gen);
|
||||
// Cross over and Mutate
|
||||
var newGenome = Learn.mutate(gen);
|
||||
|
||||
// Add to generation
|
||||
Learn.genomes.push(Network.fromJSON(newGenome));
|
||||
}
|
||||
// Add to generation
|
||||
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
|
||||
Learn.executeGeneration();
|
||||
})
|
||||
// Execute next generation
|
||||
Learn.executeGeneration();
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Sort all the genomes, and delete the worst one
|
||||
// untill the genome list has selectN elements.
|
||||
//
|
||||
Learn.selectBestGenomes = function (selectN){
|
||||
var selected = _.sortBy(Learn.genomes, 'fitness').reverse();
|
||||
var selected = _.sortBy(Learn.genomes, 'fitness').reverse();
|
||||
|
||||
while(selected.length > selectN){
|
||||
selected.pop();
|
||||
}
|
||||
while (selected.length > selectN) {
|
||||
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:
|
||||
// 1) Set's listener for sensorData
|
||||
// 2) On data read, applyes the neural network, and
|
||||
// set it's output
|
||||
// 3) When the game has ended and compute the fitness
|
||||
//
|
||||
Learn.executeGenome = function (genome, next){
|
||||
if(Learn.state == 'STOP')
|
||||
return;
|
||||
if (Learn.state == 'STOP') {
|
||||
return;
|
||||
}
|
||||
|
||||
Learn.genome = Learn.genomes.indexOf(genome) + 1;
|
||||
// Learn.ui.logger.log('Executing genome '+Learn.genome);
|
||||
Learn.genome = Learn.genomes.indexOf(genome) + 1;
|
||||
// Learn.ui.logger.log('Executing genome '+Learn.genome);
|
||||
|
||||
// Check if genome has AT LEAST some experience
|
||||
if(Learn.shouldCheckExperience){
|
||||
if(!Learn.checkExperience(genome)){
|
||||
genome.fitness = 0;
|
||||
// Learn.ui.logger.log('Genome '+Learn.genome+' has no minimum experience');
|
||||
return next();
|
||||
}
|
||||
}
|
||||
// Check if genome has AT LEAST some experience
|
||||
if (Learn.shouldCheckExperience) {
|
||||
if (!Learn.checkExperience(genome)) {
|
||||
genome.fitness = 0;
|
||||
// Learn.ui.logger.log('Genome '+Learn.genome+' has no min. experience');
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
Learn.gm.startNewGame(function (){
|
||||
Learn.gm.startNewGame(function (){
|
||||
|
||||
// Reads sensor data, and apply network
|
||||
Learn.gm.onSensorData = function (){
|
||||
var inputs = [
|
||||
Learn.gm.sensors[0].value,
|
||||
Learn.gm.sensors[0].size,
|
||||
Learn.gm.sensors[0].speed,
|
||||
];
|
||||
// console.log(inputs);
|
||||
// Apply to network
|
||||
var outputs = genome.activate(inputs);
|
||||
// Reads sensor data, and apply network
|
||||
Learn.gm.onSensorData = function (){
|
||||
var inputs = [
|
||||
Learn.gm.sensors[0].value,
|
||||
Learn.gm.sensors[0].size,
|
||||
Learn.gm.sensors[0].speed,
|
||||
];
|
||||
// console.log(inputs);
|
||||
// Apply to network
|
||||
var outputs = genome.activate(inputs);
|
||||
|
||||
Learn.gm.setGameOutput(outputs[0]);
|
||||
}
|
||||
Learn.gm.setGameOutput(outputs[0]);
|
||||
}
|
||||
|
||||
// Wait game end, and compute fitness
|
||||
Learn.gm.onGameEnd = function (points){
|
||||
Learn.ui.logger.log('Genome '+Learn.genome+' ended. Fitness: '+points);
|
||||
// Wait game end, and compute fitness
|
||||
Learn.gm.onGameEnd = function (points){
|
||||
Learn.ui.logger.log('Genome '+Learn.genome+' ended. Fitness: '+points);
|
||||
|
||||
// Save Genome fitness
|
||||
genome.fitness = points;
|
||||
// Save Genome fitness
|
||||
genome.fitness = points;
|
||||
|
||||
// Go to next genome
|
||||
next();
|
||||
}
|
||||
});
|
||||
// Go to next genome
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// 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,
|
||||
// it will return false
|
||||
//
|
||||
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
|
||||
var inputs = [0.0, 0.3, 0.2];
|
||||
var activation, state, outputs = {};
|
||||
// Inputs are default. We only want to test the first index
|
||||
var inputs = [0.0, 0.3, 0.2];
|
||||
var activation, state, outputs = {};
|
||||
|
||||
for(var k = start; k < stop; k += step){
|
||||
inputs[0] = k;
|
||||
for (var k = start; k < stop; k += step) {
|
||||
inputs[0] = k;
|
||||
|
||||
activation = genome.activate(inputs);
|
||||
state = Learn.gm.getDiscreteState(activation);
|
||||
|
||||
outputs[state] = true;
|
||||
}
|
||||
activation = genome.activate(inputs);
|
||||
state = Learn.gm.getDiscreteState(activation);
|
||||
|
||||
outputs[state] = true;
|
||||
}
|
||||
|
||||
// Count states, and return true if greater than 1
|
||||
return _.keys(outputs).length > 1;
|
||||
// Count states, and return true if greater than 1
|
||||
return _.keys(outputs).length > 1;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Load genomes saved from JSON file
|
||||
//
|
||||
Learn.loadGenomes = function (genomes, deleteOthers){
|
||||
if(deleteOthers)
|
||||
Learn.genomes = [];
|
||||
if (deleteOthers) {
|
||||
Learn.genomes = [];
|
||||
}
|
||||
|
||||
var loaded = 0;
|
||||
for(var k in genomes){
|
||||
Learn.genomes.push(Network.fromJSON(genomes[k]));
|
||||
loaded++;
|
||||
}
|
||||
var loaded = 0;
|
||||
for (var k in genomes) {
|
||||
Learn.genomes.push(Network.fromJSON(genomes[k]));
|
||||
loaded++;
|
||||
}
|
||||
|
||||
Learn.ui.logger.log('Loaded '+loaded+' genomes!');
|
||||
Learn.ui.logger.log('Loaded '+loaded+' genomes!');
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Builds a new genome based on the
|
||||
// expected number of inputs and outputs
|
||||
//
|
||||
Learn.buildGenome = function (inputs, outputs) {
|
||||
Learn.ui.logger.log('Build genome '+(Learn.genomes.length+1));
|
||||
var network = new Architect.Perceptron(inputs, 4, 4, outputs);
|
||||
return network;
|
||||
// console.log(JSON.stringify(network.toJSON()));
|
||||
// process.exit();
|
||||
Learn.ui.logger.log('Build genome '+(Learn.genomes.length+1));
|
||||
|
||||
var network = new Architect.Perceptron(inputs, 4, 4, outputs);
|
||||
|
||||
return network;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// SPECIFIC to Neural Network.
|
||||
// Those two methods convert from JSON to Array, and from Array to JSON
|
||||
//
|
||||
Learn.crossOver = function (netA, netB) {
|
||||
// Swap (50% prob.)
|
||||
if(Math.random() > 0.5){
|
||||
var tmp = netA;
|
||||
netA = netB;
|
||||
netB = tmp;
|
||||
}
|
||||
// Swap (50% prob.)
|
||||
if (Math.random() > 0.5) {
|
||||
var tmp = netA;
|
||||
netA = netB;
|
||||
netB = tmp;
|
||||
}
|
||||
|
||||
// Clone network
|
||||
netA = _.cloneDeep(netA);
|
||||
netB = _.cloneDeep(netB);
|
||||
// Clone network
|
||||
netA = _.cloneDeep(netA);
|
||||
netB = _.cloneDeep(netB);
|
||||
|
||||
// Cross over data keys
|
||||
Learn.crossOverDataKey(netA.neurons, netB.neurons, 'bias');
|
||||
// Learn.crossOverDataKey(netA.connections, netB.connections, 'weight');
|
||||
// Cross over data keys
|
||||
Learn.crossOverDataKey(netA.neurons, netB.neurons, 'bias');
|
||||
|
||||
return netA;
|
||||
return netA;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Does random mutations across all
|
||||
// the biases and weights of the Networks
|
||||
// (This must be done in the JSON to
|
||||
// prevent modifying the current one)
|
||||
//
|
||||
Learn.mutate = function (net){
|
||||
// Mutate
|
||||
Learn.mutateDataKeys(net.neurons, 'bias', Learn.mutationProb);
|
||||
|
||||
Learn.mutateDataKeys(net.connections, 'weight', Learn.mutationProb);
|
||||
// Mutate
|
||||
Learn.mutateDataKeys(net.neurons, 'bias', Learn.mutationProb);
|
||||
|
||||
Learn.mutateDataKeys(net.connections, 'weight', Learn.mutationProb);
|
||||
|
||||
return net;
|
||||
return net;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Given an Object A and an object B, both Arrays
|
||||
// of Objects:
|
||||
//
|
||||
@@ -299,33 +273,33 @@ Learn.mutate = function (net){
|
||||
// randomly (going from 0 to A.length)
|
||||
// 2) Swap values from `key` one to another,
|
||||
// starting by cutLocation
|
||||
//
|
||||
Learn.crossOverDataKey = function (a, b, key) {
|
||||
var cutLocation = Math.round(a.length * Math.random());
|
||||
var cutLocation = Math.round(a.length * Math.random());
|
||||
|
||||
var tmp;
|
||||
for(var k = cutLocation; k < a.length; k++){
|
||||
// Swap
|
||||
tmp = a[k][key];
|
||||
a[k][key] = b[k][key];
|
||||
b[k][key] = tmp;
|
||||
}
|
||||
var tmp;
|
||||
for (var k = cutLocation; k < a.length; k++) {
|
||||
// Swap
|
||||
tmp = a[k][key];
|
||||
a[k][key] = b[k][key];
|
||||
b[k][key] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Given an Array of objects with key `key`,
|
||||
// and also a `mutationRate`, randomly Mutate
|
||||
// the value of each key, if random value is
|
||||
// lower than mutationRate for each element.
|
||||
//
|
||||
Learn.mutateDataKeys = function (a, key, mutationRate){
|
||||
for(var k = 0; k < a.length; k++){
|
||||
// Should mutate?
|
||||
if(Math.random() > mutationRate)
|
||||
continue;
|
||||
for (var k = 0; k < a.length; k++) {
|
||||
// Should mutate?
|
||||
if (Math.random() > mutationRate) {
|
||||
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;
|
||||
+6
-1
@@ -66,7 +66,7 @@ There are a few files in the project:
|
||||
|
||||
- `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.
|
||||
|
||||
- `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)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
- [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 blessed = require('blessed')
|
||||
var fs = require('fs');
|
||||
|
||||
var screen = blessed.screen()
|
||||
|
||||
var UI = {};
|
||||
|
||||
|
||||
//
|
||||
// Initialize UI objects
|
||||
//
|
||||
UI.init = function (gameManipulator, learner) {
|
||||
UI.gm = gameManipulator;
|
||||
UI.learner = learner;
|
||||
UI.gm = gameManipulator;
|
||||
UI.learner = learner;
|
||||
|
||||
UI.grid = new contrib.grid({
|
||||
rows: 12,
|
||||
cols: 6,
|
||||
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',
|
||||
});
|
||||
UI.grid = new contrib.grid({
|
||||
rows: 12,
|
||||
cols: 6,
|
||||
screen: screen
|
||||
});
|
||||
|
||||
|
||||
//
|
||||
// Load Tree
|
||||
//
|
||||
UI.savesTree = UI.grid.set(9, 0, 3, 3, contrib.tree, {
|
||||
label: 'Saved Genomes',
|
||||
});
|
||||
|
||||
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 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';
|
||||
}
|
||||
});
|
||||
// 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,
|
||||
});
|
||||
|
||||
|
||||
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
|
||||
//
|
||||
UI.refreshFiles = function (){
|
||||
var files = [];
|
||||
var fileData =
|
||||
{
|
||||
name: 'Saved Files',
|
||||
extended: true,
|
||||
children: [{
|
||||
name: 'Refresh Folders'
|
||||
}]
|
||||
};
|
||||
var files = [];
|
||||
var fileData = {
|
||||
name: 'Saved Files',
|
||||
extended: true,
|
||||
children: [{
|
||||
name: 'Refresh Folders'
|
||||
}]
|
||||
};
|
||||
|
||||
// Populate tree
|
||||
UI.logger.log('Reading genomes dir...')
|
||||
var files = fs.readdirSync('./genomes');
|
||||
for(var k in files){
|
||||
if(files[k].indexOf('.json') >= 0){
|
||||
fileData.children.push({
|
||||
name: files[k],
|
||||
isFile: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Populate tree
|
||||
UI.logger.log('Reading genomes dir...')
|
||||
var files = fs.readdirSync('./genomes');
|
||||
for (var k in files) {
|
||||
if (files[k].indexOf('.json') >= 0) {
|
||||
|
||||
fileData.children.push({
|
||||
name: files[k],
|
||||
isFile: true,
|
||||
});
|
||||
|
||||
UI.savesTree.setData(fileData);
|
||||
}
|
||||
}
|
||||
|
||||
UI.savesTree.setData(fileData);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Updates data on the screen and render it
|
||||
//
|
||||
UI.render = function () {
|
||||
|
||||
//
|
||||
// Update data
|
||||
//
|
||||
UI.uiSensors.setData({
|
||||
titles: ['Distance', 'Size', 'Speed', 'Activation'],
|
||||
data: [
|
||||
Math.round(UI.gm.sensors[0].value * 100),
|
||||
Math.round(UI.gm.sensors[0].size * 100),
|
||||
Math.round(UI.gm.sensors[0].speed * 100),
|
||||
Math.round(UI.gm.gameOutput * 100),
|
||||
]
|
||||
})
|
||||
// Update data
|
||||
UI.uiSensors.setData({
|
||||
titles: ['Distance', 'Size', 'Speed', 'Activation'],
|
||||
data: [
|
||||
Math.round(UI.gm.sensors[0].value * 100),
|
||||
Math.round(UI.gm.sensors[0].size * 100),
|
||||
Math.round(UI.gm.sensors[0].speed * 100),
|
||||
Math.round(UI.gm.gameOutput * 100),
|
||||
]
|
||||
})
|
||||
|
||||
//
|
||||
// Set Genome stats and score
|
||||
//
|
||||
var learn = UI.learner;
|
||||
var uiStats = 'Status: '+learn.state+'\n';
|
||||
uiStats += 'Fitness: '+UI.gm.points+'\nGameStatus: '+UI.gm.gamestate + '\n';
|
||||
uiStats += 'Generation: '+learn.generation+' : '+learn.genome+'/'+learn.genomes.length;
|
||||
UI.uiScore.setText(uiStats);
|
||||
// Set Genome stats and score
|
||||
var learn = UI.learner;
|
||||
var uiStats = '';
|
||||
uiStats += 'Status: ' + learn.state + '\n';
|
||||
uiStats += 'Fitness: ' + UI.gm.points + '\n';
|
||||
uiStats += 'GameStatus: ' + UI.gm.gamestate + '\n';
|
||||
uiStats += 'Generation: ' + learn.generation;
|
||||
uiStats += ' : ' + learn.genome + '/' + learn.genomes.length;
|
||||
UI.uiScore.setText(uiStats);
|
||||
|
||||
if(UI.gm.gameOutput){
|
||||
var str = 'Action: '+UI.gm.gameOutputString+'\nActivation: '+UI.gm.gameOutput;
|
||||
UI.uiGenomes.setText(str);
|
||||
}else{
|
||||
UI.uiGenomes.setText('Loading...');
|
||||
}
|
||||
if (UI.gm.gameOutput) {
|
||||
var str = '';
|
||||
str += 'Action: ' + UI.gm.gameOutputString + '\n'
|
||||
str += 'Activation: ' + UI.gm.gameOutput;
|
||||
UI.uiGenomes.setText(str);
|
||||
} else {
|
||||
UI.uiGenomes.setText('Loading...');
|
||||
}
|
||||
|
||||
//
|
||||
// Render screen
|
||||
//
|
||||
screen.render();
|
||||
// Render screen
|
||||
screen.render();
|
||||
}
|
||||
|
||||
// Continuously render screen
|
||||
setInterval(UI.render, 25);
|
||||
|
||||
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 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
|
||||
//
|
||||
robot.setMouseDelay(1);
|
||||
|
||||
|
||||
//
|
||||
// Initialize Game
|
||||
//
|
||||
GameManipulator.findGamePosition();
|
||||
|
||||
|
||||
//
|
||||
// Check for found game
|
||||
//
|
||||
if(GameManipulator.offset){
|
||||
// Uncomment this line to debug the
|
||||
// starting point (Check if it's detecting it correcly)
|
||||
if (GameManipulator.offset) {
|
||||
// Uncomment this line to debug the
|
||||
// starting point of sensor (Check if it's detecting it correcly)
|
||||
|
||||
// robot.moveMouse(GameManipulator.offset[0]+GameManipulator.sensors[0].offset[0],
|
||||
// GameManipulator.offset[1] + GameManipulator.sensors[0].offset[1]);
|
||||
// robot.moveMouse(GameManipulator.offset[0]+GameManipulator.sensors[0].offset[0],
|
||||
// GameManipulator.offset[1] + GameManipulator.sensors[0].offset[1]);
|
||||
|
||||
robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]);
|
||||
}else{
|
||||
console.error('FAILED TO FIND GAME!');
|
||||
process.exit();
|
||||
robot.moveMouse(GameManipulator.offset[0], GameManipulator.offset[1]);
|
||||
} else {
|
||||
console.error('FAILED TO FIND GAME!');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Initialize UI
|
||||
//
|
||||
UI.init(GameManipulator, Learner);
|
||||
|
||||
//
|
||||
|
||||
// Init Learner
|
||||
//
|
||||
Learner.init(GameManipulator, UI, 12, 4, 0.2);
|
||||
|
||||
|
||||
//
|
||||
// Start reading game state and sensors
|
||||
//
|
||||
setInterval(GameManipulator.readSensors, 40);
|
||||
setInterval(GameManipulator.readGameState, 200);
|
||||
|
||||
|
||||
//
|
||||
// Start game (Example of API usage)
|
||||
//
|
||||
/*
|
||||
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() {
|
||||
UI.logger.log('Game HAS started!', game);
|
||||
GameManipulator.onGameEnd = function () {
|
||||
UI.logger.log('Game HAS ended!', game);
|
||||
GameManipulator.startNewGame(function() {
|
||||
UI.logger.log('Game HAS started!', game);
|
||||
GameManipulator.onGameEnd = function () {
|
||||
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');
|
||||
|
||||
// Cache screen size
|
||||
var screenSize = robot.getScreenSize();
|
||||
|
||||
// Indexes
|
||||
var X = 0;
|
||||
var Y = 1;
|
||||
|
||||
|
||||
// Create the "class" wrapper
|
||||
var scanner = {};
|
||||
var Scanner = {};
|
||||
|
||||
|
||||
//
|
||||
// Check if the given position is outside the Screen
|
||||
//
|
||||
scanner.isOutOfBound = function (pos) {
|
||||
if( pos[X] < 0 || pos[Y] < 0 ||
|
||||
pos[X] >= screenSize.width ||
|
||||
pos[Y] >= screenSize.height)
|
||||
return true;
|
||||
Scanner.isOutOfBound = function (pos) {
|
||||
if ( pos[X] < 0 || pos[Y] < 0 ||
|
||||
pos[X] >= screenSize.width ||
|
||||
pos[Y] >= screenSize.height) {
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Limits the x/y values of position to fit the screen
|
||||
//
|
||||
scanner.makeInBounds = function (pos) {
|
||||
Scanner.makeInBounds = function (pos) {
|
||||
|
||||
if(pos[X] < 0)
|
||||
pos[X] = 0;
|
||||
if (pos[X] < 0) {
|
||||
pos[X] = 0;
|
||||
}
|
||||
|
||||
if(pos[X] >= screenSize.width)
|
||||
pos[X] = screenSize.width - 1;
|
||||
if (pos[X] >= screenSize.width) {
|
||||
pos[X] = screenSize.width - 1;
|
||||
}
|
||||
|
||||
if(pos[Y] < 0)
|
||||
pos[Y] = 0;
|
||||
if (pos[Y] < 0) {
|
||||
pos[Y] = 0;
|
||||
}
|
||||
|
||||
if(pos[Y] >= screenSize.height)
|
||||
pos[Y] = screenSize.height - 1;
|
||||
if (pos[Y] >= screenSize.height) {
|
||||
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:
|
||||
returns null;
|
||||
// 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:
|
||||
// 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:
|
||||
returns null
|
||||
// (CLONE instead of using the real one)
|
||||
current = Scanner.makeInBounds([start[X], start[Y]]);
|
||||
|
||||
otherwise:
|
||||
return that point
|
||||
|
||||
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;
|
||||
if (delta[X] == 0 && delta[Y] == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
while(!scanner.isOutOfBound(current)){
|
||||
// Check current pixel
|
||||
color = robot.getPixelColor(current[X], current[Y]);
|
||||
while (!Scanner.isOutOfBound(current)) {
|
||||
// Check current pixel
|
||||
color = robot.getPixelColor(current[X], current[Y]);
|
||||
|
||||
if(!invertMatch && color.toString() == matchingColor)
|
||||
return current;
|
||||
if (!inverted && color.toString() == matchColor) {
|
||||
return current;
|
||||
}
|
||||
|
||||
if(invertMatch && color.toString() != matchingColor)
|
||||
return current;
|
||||
if (inverted && color.toString() != matchColor) {
|
||||
return current;
|
||||
}
|
||||
|
||||
current[X] += delta[X];
|
||||
current[Y] += delta[Y];
|
||||
iterations++;
|
||||
current[X] += delta[X];
|
||||
current[Y] += delta[Y];
|
||||
iterations++;
|
||||
|
||||
if(iterations > iterLimit)
|
||||
return null;
|
||||
}
|
||||
if (iterations > iterLimit) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
// Export the module
|
||||
module.exports = scanner;
|
||||
module.exports = Scanner;
|
||||
Referência em uma Nova Issue
Bloquear um usuário