I have created a little maze with a robot and I use Blockly to generate code to try to solve it. I can move the robot using Javascript commands which are Blockly blocks. So far so good.
I am currently breaking my head over arguments of if-statements and while loops. Mainly, I have tried two things:
Blockly maze
create a variable, 'not_goal_reached' which says whether or not the robot has reached the goal position (cross). Code:
function not_done() {
var goal_location = get_goal_position()
var goal_x = goal_location[0];
var goal_y = goal_location[1];
console.log('in not done');
//console.log(player.x!= goal_x || player.y != goal_y)
return (player.x!= goal_x || player.y != goal_y);
};
Blockly.Blocks['not_goal_reached'] = {
init: function() {
this.appendDummyInput()
.appendField("not at goal")
this.setOutput(true, "Boolean");
this.setColour(230);
this.setTooltip('');
this.setHelpUrl('');
}
};
Blockly.JavaScript['not_goal_reached'] = function(block) {
var code = 'not_done()';
// TODO: Change ORDER_NONE to the correct strength.
//console.log(code)
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
However, when using this block in an If or While statement. I always get a Javascript error that does not help me to find the solution:
TypeError: Cannot read property 'toBoolean' of undefined
at Interpreter.stepConditionalExpression (acorn_interpreter.js:148)
at Interpreter.step (acorn_interpreter.js:45)
at nextStep (index.html:79)
I use the Acorn js interpreter:
window.LoopTrap = 2000;
//Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = Blockly.JavaScript.workspaceToCode(workspace);
console.log(code);
var myInterpreter = new Interpreter(code, initInterpreter);
//Blockly.JavaScript.INFINITE_LOOP_TRAP = null
var counter = 0;
function nextStep() {
try {
if (myInterpreter.step()) {
counter+=1;
console.log(counter);
if (counter < window.LoopTrap) {
window.setTimeout(nextStep, 30);
}
else {
throw "Infinite Loop!"
}
}
}
catch (e) {
//alert(e);
console.log(e)
}
}
nextStep();
Problem: javascript error I can not solve :(
I created my own While block that does not require input. This While block checks internally whether or not the robot has reached the goal and then processes the DO statements:
Blockly.Blocks['repeat_forever'] = {
init: function() {
this.appendDummyInput()
.appendField("While not at goal");
this.appendStatementInput("DO")
.appendField("Do");
this.setPreviousStatement(true);
this.setColour(230);
this.setTooltip('');
this.setHelpUrl('');
}
};
Blockly.JavaScript['repeat_forever'] = function(block) {
var branch = Blockly.JavaScript.statementToCode(block, 'DO');
// TODO: Assemble JavaScript into code variable.
//if (Blockly.JavaScript.INFINITE_LOOP_TRAP) {
// branch = Blockly.JavaScript.INFINITE_LOOP_TRAP.replace(/%1/g,
// '\'block_id_' + block.id + '\'') + branch;
// console.log(branch);
//}
var code = 'while (' + not_done() + ') {' + branch + '}';
console.log(code)
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
This works, BUT, here I have the problem that my internal function 'not_done' is only evaluated once (at code generation) to while(true) (since the first time the robot is of course not at the goal location yet). This block correctly applies the DO codes but does not halt (since while (true)). If I add quotes around 'not_done()' the function is evaluated once apparently, but then I receive the same Javascript error as above (Cannot read property 'toBoolean' of undefined)
Am I missing something here? Thanks a lot for your time!
Greetings
K
It seems that you setTimeout which cannot be reached while the while loop runs.
Related
Little info, i have an arp.js file which takes a subnet address "192.168.2" and gets all strings returned from arp -a and stores in an array.
I can't figure out why my arpList function is returning an undefined value in my index.js file.
All the console.logs are returning the correct values in the arp.js page when called from the index.js, but the ipObj is coming up undefined. Even the console.log before i return of ipObj works.
Any help would be greatly appreciated.
var { spawn } = require('child_process');
const arpLs = spawn('arp', ['-a']);
var bufferData;
module.exports = {
arpList: function (subnet) {
arpLs.stdout.on('data', data => {
bufferData += data
})
arpLs.stderr.on('data', data => {
console.log('error: ' + data);
});
arpLs.on('exit', function (code) {
if (code != 0) {
console.log("Error exiting"); //if error occurs
}
console.log("exit start 1"); // checking internal processes at stages
var dataArray = bufferData.split(' ');
var ipArray = [];
for (i = 0; i < dataArray.length; i++) {
if (dataArray[i].includes(subnet)) {
ipArray.push(dataArray[i]);
console.log("loop working");
}
}
var ipObj = { "lanIps": ipArray };
console.log("Object is there: "+ipObj)
return ipObj; // this obj should be returned to the index.js call using
})
},
sayMyName: function () {
return "Hello";
}
}
//arpList(ipSubnet);
//INDEX.js
//the index page looks like this
//var arp = require('./arp.js);
//var ipSubnet = "192.168.2";
//var lanIps = arp.arpList(ipSubnet);
//console.log(lanIps);
I ended up adding a callback function to arpList - function (subnet, callback)
Then instead of returning the value pass it into the callback
Then on the index.js side instead of
var lanIps = arp.arpList(value)
i used
arp.arpList(value, function(res){lanIps = res}
return ipObj; // this obj should be returned to the index.js call using
It won't be returned. The reference say nothing about return value. Node-style callbacks rarely work like that because they are potentially asynchronous and returned value cannot be taken into account.
This a special case of this well-known problem. The process is asynchronous and is finished after arp.arpList(ipSubnet) call, there's nothing to assign to lanIps. This is a use case for promises. There are already third-party promisified counterparts like child-process-promise.
The problem can be also solved by moving to synchronous API. child_process functions have synchronous counterparts, including spawnSync.
In the code below, I am trying to get a value from a 'nodehun' method called spellSuggestions. In the documentation I'm told the syntax to use this method is as follows: dict.spellSuggestions(w, handleMisspellings);
where w is a list of words and handleMisspellings is a function (which is posted below). I can see the output on the console for handleMisspellings, but for the life of me, I cannot return or find a way to return a variable from this call: [dict.spellSuggestions(w, handleMisspellings);]. After setting a var equal to 'dict.spellSuggestions(w, handleMisspellings);' the return value is undefined. Please help!
var debugFlag = process.argv.indexOf('debug') > -1;
var nodehun = require('./../build/' + (debugFlag ? 'Debug' : 'Release') + '/nodehun');
var fs = require('fs');
var dict = new nodehun(fs.readFileSync(__dirname+'/dictionaries/en_US.aff'),fs.readFileSync(__dirname+'/dictionaries/en_US.dic'));
//var words = ['original', 'roach', 'erasee', 'come', 'consol', 'argumnt', 'gage',
// 'libary', 'lisence', 'principal', 'realy', 'license', 'suprise', 'writting'];
var handleMisspellings = function(err, correct, suggestions, origWord, callback) {
if (err) throw err;
if (correct) {
console.log(origWord + ' is spelled correctly!');
}
else {
console.log(origWord + ' not recognized. Suggestions: ' + suggestions);
}
var value = {
err: err,
correct: correct,
suggestions: suggestions,
origWord: origWord
};
console.log('VALUE+++++: ' + value);
return value;
}
var foo = function(words) {
words.forEach(function(w) {
dict.spellSuggestions(w, handleMisspellings);
some = dict;
console.log(JSON.stringify(some, null, 2));
});
}
module.exports = {
foo: foo
}
Thanks Dave. I eventually discovered the practical use of callback functions. For each method that contained data that I desired to access outside of the method, I declared an individual function to wrap the method. The function accepted two input arguments. The first was the input variable to drive the method call. The second was literally 'callback'. Inside the method, I would perform whatever operation I wanted to package the data into a JSON object before returning any desired data with 'return callback(var)'. In the call of the created wrapper function, I would actually call the function using the input variable of choice to drive the method in the function definition, and pass 'function(return_variable)' as the second argument. This creates a new method in which the desired data may be accessed or even again called back. The final code I desired performs a for loop on each element of a list of words, creates metadata about those words, and appends the unique data for each word to each word in a single array. The final array is a single object which contains all input words, and all data about those words. It required 4 individual functions (one of which was recursive), and a function call. Please see the code snippet of the function described above [doCall]. Note the use of the code begins at the call of 'analyze' [which is commented out here] and works its way up to each previous function declaration. I hope this helps someone else in the future to understand the functional use of 'callbacks'. Please ask if you have any questions, and Thanks again =D.
function doCall(word, callback) {
dict.spellSuggestions(word, function(err, correct, suggestions, origWord) {
if (err) throw err;
// if (correct)
// console.log(origWord + ' is spelled correctly!');
// else
// console.log(origWord + ' not recognized. Suggestions: ' + suggestions);
val = {
err: err,
correct: correct,
origWord: origWord,
suggestions: suggestions
}
return callback(val);
});
}
function addMember(array, index, callback){
doCall(array[index], function(val){
// console.log(val);
// console.log(index);
// console.log(array[index]);
// console.log(val.origWord);
array[val.origWord] = val;
// console.log(array[val.origWord]);
index = index + 1;
return callback(array, index);
});
}
function Loop(array, index, callback) {
addMember(array, index, function(array2, index2){
// console.log(index);
// console.log(index2);
if(index2 === array2.length) {
return callback(array2);
}
else{
Loop(array2, index2, callback);
}
});
}
function analyze(array, index, callback){
Loop(array, index, function(complete_array){
console.log('!!!!!!!!!!!!!!!!!' + complete_array);
return callback(complete_array);
});
}
/*
analyze(words, 0, function(complete_array){
// for(i = 0; i < complete_array.length; i++) {
console.log(complete_array);
// }
});
*/
module.exports = {
analyze
}
Is there a way to make Node.js stream as coroutine.
Example
a Fibonacci numbers stream.
fibonacci.on('data', cb);
//The callback (cb) is like
function cb(data)
{
//something done with data here ...
}
Expectation
function* fibonacciGenerator()
{
fibonacci.on('data', cb);
//Don't know what has to be done further...
};
var fibGen = fibonacciGenerator();
fibGen.next().value(cb);
fibGen.next().value(cb);
fibGen.next().value(cb);
.
.
.
Take desired numbers from the generator. Here Fibonacci number series is just an example, in reality the stream could be of anything a file, mongodb query result, etc.
Maybe something like this
Make the 'stream.on' function as a generator.
Place yield inside the callback function.
Obtain generator object.
Call next and take the next value in stream.
Is it at-least possible if yes how and if not why? Maybe a dumb question :)
If you don't want to use a transpiler (e.g. Babel) or wait until async/await make it to Node.js, you can implement it yourself using generators and promises.
The downside is that your code must live inside a generator.
First, you can make a helper that receives a stream and returns a function that, when called, returns a promise for the next "event" of the stream (e.g. data).
function streamToPromises(stream) {
return function() {
if (stream.isPaused()) {
stream.resume();
}
return new Promise(function(resolve) {
stream.once('data', function() {
resolve.apply(stream, arguments);
stream.pause();
});
});
}
}
It pauses the stream when you're not using it, and resumes it when you ask it the next value.
Next, you have a helper that receives a generator as an argument, and every time it yields a promise, it resolves it and passes its result back to the generator.
function run(fn) {
var gen = fn();
var promise = gen.next().value;
var tick = function() {
promise.then(function() {
promise = gen.next.apply(gen, arguments).value;
}).catch(function(err) {
// TODO: Handle error.
}).then(function() {
tick();
});
}
tick();
}
Finally, you would do your own logic inside a generator, and run it with the run helper, like this:
run(function*() {
var nextFib = streamToPromises(fibonacci);
var n;
n = yield nextFib();
console.log(n);
n = yield nextFib();
console.log(n);
});
Your own generator will yield promises, pausing its execution and passing the control to the run function.
The run function will resolve the promise and pass its value back to your own generator.
That's the gist of it. You'd need to modify streamToPromises to check for other events as well (e.g. end or error).
class FibonacciGeneratorReader extends Readable {
_isDone = false;
_fibCount = null;
_gen = function *() {
let prev = 0, curr = 1, count = 1;
while (this._fibCount === -1 || count++ < this._fibCount) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
return curr;
}.bind(this)();
constructor(fibCount) {
super({
objectMode: true,
read: size => {
if (this._isDone) {
this.push(null);
} else {
let fib = this._gen.next();
this._isDone = fib.done;
this.push(fib.value.toString() + '\n');
}
}
});
this._fibCount = fibCount || -1;
}
}
new FibonacciGeneratorReader(10).pipe(process.stdout);
Output should be:
1
1
2
3
5
8
13
21
34
55
What I'm trying to do is this, I have 2 users an attacker and a defender. I want the call hit() function until one of the runs out of Health, hit() should be called on turns, first attacker, then defender, then attacker and so on until one reaches 0 hp.
My idea was to do it with a while loop, with current code all I get is the console.log from hit(), an infinite loop. The data from hit() is returned correctly if it's not inside a loop ... I could simply just work in the while loop and not use the hit function but it would mean repeating a lot of code, since there will be a few things to consider before a hit actually happens.
If you have an alternative to the while loop I'm open to ideas, also I should mention I'm new at node so keep it as simple as possible please. Thank you.
This is the relevant part of the code:
var prepareAttack = function(attacker,defender) {
connectdb().done(function(connection) {
query(connection,'SELECT * FROM members WHERE name = ?', attacker).done(function(result) {
var attackerData = result[0][0]
query(connection,'SELECT * FROM members WHERE name = ?', defender).done(function(result) {
var defenderData = result[0][0]
var attackerHp = attackerData.hp
var defenderHp = defenderData.hp
while(attackerHp > 0 && defenderHp > 0) {
hit(attackerData,defenderData).done(function(result){
defenderHp = result;
console.log(defenderHp)
})
hit(defenderData, attackerData).done(function(result) {
attackerHp = result;
console.log(attackerHp)
})
}
})
})
})
}
var hit = function (attackerData, defenderData) { // simplified code just relevant parts inside.
console.log('hitting')
var defer = Q.defer()
var newHp = 0;
defer.resolve(newHp)
return defer.promise
}
Hi I have a backbone web app using Jquery and NodeJs/mongo as the server side framework. I'm having problems with making a http get call with a foreah loop and the results of the get call being iteratively added to each row of the loop.
var eventid = this.model.get("_id");
var inPromise = $.get("/registrants/list?eventid="+eventid,null,null,"json").then(
function (result){
var temp;
var finalVal = '';
var tempfinalVal = "";
var loop = 0
percentage = 0;
$.each(result.registrants,function(index,registrant){
temp = JSON.parse(registrant.fields);
for (var key in temp) {
if(key =="Email"){
if(temp[key] != ""){
$.get("/stats/registrant?userid="+temp[key]+"&eventid="+eventid,null,null,"json").then(function(result2){
percentage = (result2.Stats.type ===undefined || result2.Stats.type ==null) ? "0": result2.Stats.type;
finalVal +=percentage+"\n";
}).fail(function(){
percentage = "0";
});
}
}else if(key =="eventid"){
loop++;
finalVal = finalVal.slice(0, - 1);
finalVal +='\n';
}
finalVal +=temp[key] + ',';
}
});
//promises.push(inPromise);
}
).done(function(finalVal){
$("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(finalVal));
console.log("DONE");
}).fail(function(){
console.log("fail");
});
// promise.done(function () {
// console.log(" PROMISE DONE");
// });
So I have the loop through a collection and the last item of the docuemnt gets a content froma nother http call and when all is fone it will create a CSV file. The problem is that THE "DONE" text echos firts then the "CALL" text is displayed
Rick, your problem is not the simplest due to :
the need for nested asynchronous gets
the need to build each CSV data row partly synchronously, partly asynchronously.
the need for a mechanism to handle the fulfilment of multiple promises generated in the inner loop.
From what you've tried, I guess you already know that much.
One important thing to note is that you can't rely on for (var key in temp) to deliver properties in any particular order. Only arrays have order.
You might try something like this :
var url = "/stats/registrant",
data = { 'eventid': this.model.get('_id') },
rowTerminator = "\n",
fieldNames = ['firstname','lastname','email','company','score'];
function getScore(email) {
return $.get(url, $.extend({}, data, {'userid':email}), null, "json").then(function(res) {
return res.Stats ? res.Stats.type || 0 : 0;
}, function() {
//ajax failure - assume score == 0
return $.when(0);
});
}
$.get("/registrants/list", data, null, "json").then(function(result) {
var promises = [];//An array in which to accumulate promises of CSV rows
promises.push($.when(fieldNames)); //promise of CSV header row
if(result.registrants) {
$.each(result.registrants, function(index, registrant) {
if(registrant.fields) {
// Synchronously initialize row with firstname, lastname, email and company
// (omitting score for now).
var row = fieldNames.slice(0,-1).map(function(fieldName, i) {
return registrant.fields[fieldName] || '';
});
//`row` remains available to inner functions due to closure
var promise;
if(registrant.fields.Email) {
// Fetch the registrant's score ...
promise = getScore(registrant.fields.Email).then(function(score) {
//... and asynchronously push the score onto row
row.push(score);
return row;
});
} else {
//or synchronously push zero onto row ...
row.push(0);
//... and create a resolved promise
promise = $.when(row);
}
promises.push(promise);//Accumulate promises of CSV data rows (still in array form), in the correct order.
}
});
}
return $.when.apply(null, promises).then(function() {
//Join all the pieces, in nested arrays, together into one long string.
return [].slice.apply(arguments).map(function(row) {
return row.join(); //default glue is ','
}).join(rowTerminator);
});
}).done(function(str) {
$("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(str));
console.log("DONE");
}).fail(function() {
console.log("fail");
});
partially tested
See comments in code for explanation and please ask if there's anything you don't follow.