my strings will not be re-declared in my loop - javascript

I have been programming a Battlesnake (https://play.battlesnake.com/) and I need to re-declare my move commands (like move="left" and move="right" for example) command but every time it goes over the code it uses the move command once and won't use it again. It also only uses the first one it can and won't use any other ones above it. Loops won't work because to my understanding the Battlesnake servers call the handleMove function each turn and if the Battlesnake servers don't get a response within 500 milliseconds the game will move for you. The code starts at the top of the handleMove and ends at the response.status thing. I know the code goes through the functions because I tracked it with console.log. The code goes through everything as intended, because I set console.log on all of the functions and inside all the if commands, and it runs through the move strings inside the if commands, but ignores all move commands it reaches after the code gets to response.status. That is where I am confused. A lot of the data is hosted on the Battlesnake servers, so if something is not declared in my code it probably came from the Battlesnake servers.
How can I fix this?
const express = require('express')
const PORT = process.env.PORT || 3000
const app = express()
app.use(bodyParser.json())
app.get('/', handleIndex)
app.post('/start', handleStart)
app.post('/move', handleMove)
app.post('/end', handleEnd)
app.listen(PORT, () => console.log(`Battlesnake Server listening at http://127.0.0.1:${PORT}`))
function handleIndex(request, response) {
var battlesnakeInfo = {
apiversion: '1',
author: '',
color: '#1c607d',
head: 'caffeine',
tail: 'leaf'
}
response.status(200).json(battlesnakeInfo)
}
function handleStart(request, response) {
var gameData = request.body
console.log('START')
response.status(200).send('ok')
}
function handleMove(request, response) {
var gameData = request.body
var head = gameData.you.head
var boardWidth = gameData.board.width
var boardHeight = gameData.board.height
var food = gameData.board.food[0]
// code i wrote
function moveUpB() {
if (head.x < 1) {
move = "up"
//refuses to execute at all??
}
}
function moveRightB() {
if (head.y > (boardHeight - 2)) {
move = "right"
//only used once
}
}
function moveDownB() {
if (head.x > (boardWidth - 2)) {
move = "down"
//only used once
}
}
function moveLeftB() {
if (head.y < 1) {
move = "left"
//only used once
}
}
moveUpB()
moveRightB()
moveDownB()
moveLeftB()
//aafter here it should go back to moveUpB but it doesent?
//rest of the code needed for the snake to move
console.log('MOVE: ' + move)
response.status(200).send({
move: move
})
}
function handleEnd(request, response) {
var gameData = request.body
console.log('END')
response.status(200).send('ok')
}

Related

Netlify Serverless Function returning 404

I am trying to set up a simple serverless function on Netlify just to test out usage of environment variables. I have defined the following two environment variables in Netlify for my site:
Variable Name
Value
ALPHABET_SEPARATION
2
CHARS_BETWEEN
3
I have also updated my functions directory as follows:
Functions directory: myfunctions
I am using continuous deployment from github. As I do not know the use of npm at present and finding it convenient to directly test the production deploy, I have defined a subdirectory called myfunctions inside my root directory and have placed my javascript file containing the "serverless" function inside it on my local machine. I have built in logic so that the "serverless" function gets called only when a "netlify" flag is set, otherwise, an alternate function gets executed client-side. Basically it works as follows:
const deploy = "netlify" //Possible valid values are "local" and "netlify"
async function postRandomString() {
const stringToUpdate = "THISISATESTSTRING"
var stringToPost = "DUMMYINITIALVALUE";
if (deploy === "local") {
stringToPost = updateString(stringToUpdate); //updateString is a function defined elsewhere and executes client-side;
}
else if (deploy === "netlify") {
const config = {
method: 'GET',
headers: {
'Accept': 'application/json',
}
};
const res = await fetch(`myfunctions/serverUpdateString?input=${stringToUpdate}`, config);
const data = await res.json();
stringToPost = data.retVal;
console.log(data.retVal);
}
else {
stringToPost = "##ERROR##";
}
postString(stringToPost); //postString is a function defined elsewhere and executes client-side;
}
The serverless function file serverUpdateString.js is coded as follows (it basically sets a character at a certain position (determined by CHARS_BETWEEN) in the string to an alphabetical character which is a certain number (determined by ALPHABET_SEPARATION) of places in the alphabet after the first character of the string (don't ask why - the point is that it never even receives/handles the request):
exports.handler = async function (event) {
const { CHARS_BETWEEN, ALPHABET_SEPARATION } = process.env;
const charsBetween = CHARS_BETWEEN;
const alphabetSeparation = ALPHABET_SEPARATION;
const initString = event.queryStringParameters.input;
const rootUnicode = initString.charCodeAt(0);
const finalUnicode = "A".charCodeAt(0) + (rootUnicode - "A".charCodeAt(0) + alphabetSeparation) % 26;
const finalChar = String.fromCharCode(finalUnicode);
const stringArray = initString.split("");
stringArray[charsBetween + 1] = finalChar;
const stringToReturn = stringArray.join("");
const response = {
statusCode: 200,
retVal: stringToReturn,
}
return JSON.stringify(response);
}
When I run it, I get a 404 error for the GET request:
In the above image, script.js:43 is the line const res = await fetch(myfunctions/serverUpdateString?input=ATESTSTRIN, config); in the calling file, as shown in the first code block above.
What am I doing incorrectly? Surely Netlify should be able to pick up the serverless function file given that I have specified the folder alright and have placed it at the right place in the directory structure? I have given the whole code for completeness but the problem seems quite elementary. Look forward to your help, thanks.
I got assistance from Netlify forums. Basically the following changes needed to be made:
The fetch request -- line 43 in the calling code (script.js) -- needed to be changed to
const res = await fetch(`https://netlifytestserverless.netlify.app/.netlify/functions/serverUpdateString?input=${stringToUpdate}`, config);
The return statement in the lambda function needed to be changed to:
const response = {
statusCode: 200,
body: JSON.stringify(stringToReturn),
}
Other minor changes such as using parseInt with the environment variables.
The code works now.

Call API at scheduled time

I was trying to run an API call at a scheduled time. I researched through sites and found this package called node-schedule from npmjs. This is working as expected by calling the code at required time. The issue I am having is :
Suppose I have a list of times eg: ["10:00","11:00","13:00"]
Once I start the server, it will get executed at required times. But what if I want to change the time list dynamically?
Exactly what I am trying to do:
Call API and get times from Database
Setup cron-schedule for each of these times.
Dynamically adding new time to database
What I want: Dynamically add this newly added time to the cron-schedule
index.js
const express = require('express');
const schedule = require('node-schedule');
const app = express();
const port = 5000;
var date = new Date(2019, 5, 04, 14, 05, 20);// API call here
var j = schedule.scheduleJob(date, function(){
console.log('The world is going to end today.');
});
app.get('/test', (req, res) => {
var date = new Date(2019, 5, 04, 14, 11, 0); // Will call API here
var q = schedule.scheduleJob(date, function(){
console.log('Hurray!!');
});
res.send('hello there');
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
The code written above is what I have and its pretty messed up. What I am conveying there is that, while running the index.js file API is called and the cron-schedule gets executed. Now if there is some new values added to DB I want to rerun this.
Rerunning index.js is another option I have, but I don't think its the right thing to do. The next option I have in mind is to call another endpoint which is mentioned as /test above, which would eventually run the cron again.
Please let me know some suggestions or some sort of solution to this so I can rectify my mistakes.
With this code I think you could do what you want, although you must adapt it to your needs in terms of defining the functions that will execute the tasks or if you need to specify the times in which they will be executed in another way (setting specific days of the week, for example).
var times = [];
var tasks = [];
function addTask(time, fn) {
var timeArr = time.split(':');
var cronString = timeArr[1] + ' ' + timeArr[0] + ' * * *';
// according to https://github.com/node-schedule/node-schedule#cron-style-scheduling
var newTask = schedule.scheduleJob(cronString, fn);
// check if there was a task defined for this time and overwrite it
// this code would not allow inserting two tasks that are executed at the same time
var idx = times.indexOf(time);
if (idx > -1) tasks[idx] = newTask;
else {
times.push(time);
tasks.push(newTask);
}
}
function cancelTask(time) {
// https://github.com/node-schedule/node-schedule#jobcancelreschedule
var idx = times.indexOf(time);
if (idx > -1) {
tasks[idx].cancel();
tasks.splice(idx, 1);
times.splice(idx, 1);
}
}
function init(tasks) {
for (var i in tasks){
addTask(i, tasks[i]);
}
}
init({
"10:00": function(){ console.log("It's 10:00"); },
"11:00": function(){ console.log("It's 11:00"); },
"13:00": function(){ console.log("It's 13:00"); }
});
app.post('/addTask', (req, res) => {
if (!req.body.time.match(/^(0[0-9]|1[0-9]|2[0-3]|[0-9]):[0-5][0-9]$/)) {
// regex from https://stackoverflow.com/a/7536768/8296184
return res.status(400).json({'success': false, 'code': 'ERR_TIME'});
}
function fn() {
// I suppose you will not use this feature just to do console.logs
// and not sure how you plan to do the logic to create new tasks
console.log("It's " + req.body.time);
}
addTask(req.body.time, fn);
res.status(200).json({'success': true});
});

Socket.io emitting values inside ES6 class

I wonder if any smart individuals could show me how to implement Socket.IO in an OOP environment with ES6 classes. The main problem I keep running into with Socket.io is passing around the server object, in my case called 'io'. Almost every example I've seen of socket.io has been pure spaghetti code, one file with many socket related events and logic. First I tried to pass the server object, io, to new class's constructor, but for some reason you end up with a nasty "RangeError: Maximum call stack size exceeded" error message. Then I've tried to wrap my classes in module.exports function which parameter should contain the io object. Which is fine for the first class. Let's say I pass the io object into my Game, great works as expected. But when I try to reference the io object down to the Round class(Game holds an array of Rounds) I can't. Because that is one hell of a bad practice in NodeJS, require should be global and not inside the modules/functions. So I'm once again back with the same issue.
app.js(where I require the main sockets file)
const io = socketio(server, { origins: '*:*' });
...
require('./sockets')(io);
sockets/index.js(where I initialize my game server, and handle incoming messages from client sockets)
const actions = require('../actions.js');
const chatSockets = require('./chat-sockets');
const climbServer = require('./climb-server');
const authFunctions = require('../auth-functions');
module.exports = (io) => {
io.on('connection', (client) => {
console.log('client connected...');
// Standard join, verify the requested room; if it exists let the client join it.
client.on('join', (data) => {
console.log(data);
console.log(`User ${data.username} tries to join ${data.room}`);
console.log(`Client joined ${data.room}`);
client.join(data.room);
});
client.on('disconnect', () => {
console.log('Client disconnected');
});
client.on(actions.CREATE_GAME, (hostParticipant) => {
console.log('CREATE_GAME', hostParticipant);
// Authorize socket sender by token?
// Create a new game, and set the host to the host participant
climbServer.createGame(io, hostParticipant);
});
client.on(actions.JOIN_GAME, (tokenizedGameId) => {
console.log('JOIN_GAME');
const user = authFunctions.getPayload(tokenizedGameId.token);
// Authorize socket sender by token?
// Create a new game, and set the host to the host participant
const game = climbServer.findGame(tokenizedGameId.content);
game.joinGame(user);
});
});
};
climbServer.js(My game server that keeps track of active games)
const actions = require('../actions.js');
const Game = require('../models/game');
const climbServer = { games: { }, gameCount: 0 };
climbServer.createGame = (io, hostParticipant) => {
// Create a new game instance
const newGame = new Game(hostParticipant);
console.log('New game object created', newGame);
// Store it in the list of game
climbServer.games[newGame.id] = newGame;
// Keep track
climbServer.gameCount += 1;
// Notify clients that a new game was created
io.sockets.in('climb').emit(actions.CLIMB_GAME_CREATED, newGame);
};
climbServer.findGame = gameId => climbServer.games[gameId];
module.exports = climbServer;
Game.js(ES6 class that SHOULD be able to emit to all connected sockets)
const UUID = require('uuid');
const Round = require('./round');
class Game {
// Constructor
constructor(hostParticipant) {
this.id = UUID();
this.playerHost = hostParticipant;
this.playerClient = null;
this.playerCount = 1;
this.rounds = [];
this.timestamp = Date.now();
}
joinGame(clientParticipant) {
console.log('Joining game', clientParticipant);
this.playerClient = clientParticipant;
this.playerCount += 1;
// Start the game by creating the first round
return this.createRound();
}
createRound() {
console.log('Creating new round at Game: ', this.id);
const newRound = new Round(this.id);
return this.rounds.push(newRound);
}
}
module.exports = Game;
Round.js(ES6 class that is used by the Game class(stored in a rounds array))
const actions = require('../actions.js');
class Round {
constructor(gameId) {
console.log('Initializing round of gameId', gameId);
this.timeLeft = 60;
this.gameId = gameId;
this.winner = null;
this.timestamp = Date.now();
// Start countdown when class is instantiated
this.startCountdown();
}
startCountdown() {
const countdown = setInterval(() => {
// broadcast to every client
io.sockets.in(this.gameId).emit(actions.ROUND_TIMER, { gameId: this.gameId, timeLeft: this.timeLeft });
if (this.timeLeft === 0) {
// when no time left, stop counting down
clearInterval(countdown);
this.onRoundEnd();
} else {
// Countdown
this.timeLeft -= 1;
console.log('Countdown', this.timeLeft);
}
}, 1000);
}
onRoundEnd() {
// Evaluate who won
console.log('onRoundEnd: ', this.gameId);
}
}
module.exports = Round;
TO SUMMARIZE with a question: How can I pass a reference of io to my classes so that I'm able to emit to connected sockets within these classes?
This doesn't necessarily have to be ES6 classes, it can be NodeJS objects using the .prototype property. I just want a mainatainable way to handle my game server with sockets... ANY HELP IS APPRECIATED!
After hours upon hours I figured out a solution. If anyone runs into the same thing check my solution out below. Not the best, but much better than putting all socket related code in one file...
Game.js(ES6 Class). Focus on the first line containing 'module.exports'.
const GameFactory = require('../models/game');
const climbServer = { games: { }, gameCount: 0 };
climbServer.createGame = (io, hostParticipant) => {
// Create a new game instance
const Game = GameFactory(io);
const newGame = new Game(hostParticipant);
console.log('New game object created', newGame);
// Store it in the list of game
climbServer.games[newGame.id] = newGame;
// Keep track
climbServer.gameCount += 1;
return newGame;
};
climbServer.findGame = gameId => climbServer.games[gameId];
module.exports = climbServer;
The trick is to use this factory pattern where you first declare:
const GameFactory = require('../models/game');
Then initialize the factory with passing in the Socket.io server object, in my case 'io'. IF YOU pass it in via the constructor you end up with a RangeError, therefore this is the only way. Once again not certain how this code performs in comparison to spaghetti code.
const Game = GameFactory(io);
Finally, you can now instantiate instances of your class:
const newGame = new Game(hostParticipant);
If anyone have improvements or thoughts, please leave me a comment. Still uncertain about the quality of this code.

Underscore.js throttle not working

Objective
Figure what is wrong with my code, or if underscore.js throttle works as it should.
Background
I have a huge list of postal codes in a file, and I am reading those codes and pasting them on console.
I am trying to use Underscore.js throttle() function, however my code stops after two runs (even though I have dozens), and the rest of the values are never printed.
Code
My code is in a very simple NodeJS project. I created a MCVE of the situation I am facing:
"use strict";
//requiremetns
let fs = require('fs');
let readLine = require('readline');
let _ = require('underscore');
//constants
const INPUT_FILE = 'dataset.txt';
const RADIX_CONVERSATION = 10;
const THROTTLE_DELAY = 500;
let init = function() {
let lineReader = readLine.createInterface({
input: fs.createReadStream(INPUT_FILE),
output: process.stdout,
terminal: false
});
let throttledRequestFn = _.throttle(requestFn, THROTTLE_DELAY);
lineReader.on('line', function(line) {
line = line.trim();
if (_.isNaN(parseInt(line, RADIX_CONVERSATION))) {
//Do some stuff
}
else {
throttledRequestFn('mahCountry', line);
}
});
};
let requestFn = function(country, postalCode){
console.log('request for ' + country + ' and postal ' + postalCode + ' done');
return false;
};
init();
Here I first start by reading the file, one line at a time. Then if the line I am reading is a number, I print something, otherwise nothing.
Following is a test file:
Vietnam
000000
100000
160000
170000
180000
200000
220000
230000
240000
250000
260000
270000
280000
290000
300000
310000
320000
330000
350000
360000
380000
390000
400000
410000
420000
430000
440000
460000
480000
510000
520000
530000
550000
560000
570000
580000
590000
600000
620000
630000
640000
650000
660000
670000
700000
790000
800000
810000
820000
830000
840000
850000
860000
870000
880000
890000
900000
910000
920000
930000
940000
950000
960000
970000
Question
The way I see it, my code should make 2 requests per second, with a 500 ms of delay between each one. It should print all the codes in the test file.
However, I never seen anything past the second value! Why is this happening?
The throttle function is working as intended. From the documentation:
Useful for rate-limiting events that occur faster than you can keep up with.
This means that your wrapped function will probably be called less often than you want.
What you actually want is probably some kind of queue. Underscore doesn't provide one, but the async library does: http://caolan.github.io/async/docs.html#.queue
let fs = require('fs');
let readLine = require('readline');
let _ = require('async');
// import the async library
let async = require('async');
const INPUT_FILE = 'dataset.txt';
const RADIX_CONVERSATION = 10;
// create the queue
var q = async.queue(function(task, callback) {
// make sure the queue task calls the callback only after the THROTTLE_DELAY
setTimeout(function () {
requestFn(task.country, task.postalCode);
callback();
}, THROTTLE_DELAY);
}, 1)
q.drain = function () {
console.log('all items have been processed');
};
let init = function () {
let lineReader = readLine.createInterface({
input: fs.createReadStream(INPUT_FILE),
output: process.stdout,
terminal: false
});
lineReader.on('line', function(line) {
line = line.trim();
if (_.isNaN(parseInt(line, RADIX_CONVERSATION))) {
// Do some stuff
}
else {
// Add the line to the Queue, to be executed later
q.push({country: 'mahCountry', postalCode: line});
}
});
};
let requestFn = function(country, postalCode){
console.log('request for ' + country + ' and postal ' + postalCode + ' done');
return false;
};
init();
Notice the use of setTimeout in the function that handles the elements in the queue. That way, you'll still only make one request every 500ms, but will be guaranteed to make all of them.

Loop with socket.io terminates at the beginning

I'm building this function for upload to the server small tile images.
The client builds the tileBuffer and then calls the fireTiles function.
Here I would like to build a loop based on the tileBuffer.length. The server will handle the control. So, i emit StartAddTiles and I immediately called back from the server with the AnotherTile event. The debugger shows me I've been called by the server and I see the code going into the socket.on('AnotherTile'... sentence.
The problem is that when the code reaches the AddTile emit function, it stops there and nothing happens. The server does not receive the request and the loop is terminated there.
Where is the error in my code?
function fireTiles (tileBuffer, mapSelected) {
var tiles = tileBuffer.length;
var tBx = 0;
try
{
var socket = io.connect('http://myweb:8080/');
socket.emit('StartAddTiles', tiles, mapSelected);
socket.on('AnotherTile', function (tlN){
if (tlN < tiles) {
var data = tileBuffer[tlN]; //uso tlN per far comandare il server
tBx++; // debug purpose
socket.emit('AddTile', mapSelected, data, tBx);
} else {
// something went wrong
alert('Error calculating tiles');
return;
}
});
}
catch(err)
{
document.getElementById('status').innerHTML = err.message;
}
}
Here is the server side:
io.sockets.on('connection', function(client) {
console.log('Connecting....');
// controls are limited, this is just a beginning
// Initiate loop
client.on('StartAddTiles', function(tiles, mapSelected) {
var mapId = mapSelected;
mapLoading[mapId] = { //Create a new Entry in The mapLoading Variable
tilesToLoad : tiles,
tilesLoaded : 0
}
console.log('Start loading '+mapLoading[mapId].tilesToLoad+' tiles.');
// Ask for the first tile
client.emit('AnotherTile', mapLoading[mapId].tilesLoaded);
//
});
// client add new Tile/Tiles
client.on('addTile', function(mapSelected, data, tBx) {
var mapId = mapSelected;
mapLoading[mapId].tilesLoaded = ++1;
console.log('Adding tile '+mapLoading[mapId].tilesLoaded+' of '+mapLoading[mapId].tilesToLoad+' tBx '+tBx);
// insert Tile
db_manager.add_tiles(tileBuffer, function(result) {
if (mapLoading[mapId].tilesLoaded == mapLoading[mapId].tilesToLoad) { // full map loaded
mapLoading[mapId] = ""; //reset the buffer
client.emit('TilesOk', mapLoading[mapId].tilesLoaded);
} else {
console.log('requesting tile num: '+mapLoading[mapId].tilesLoaded);
client.emit('AnotherTile', mapLoading[mapId].tilesLoaded);
}
//
});
});
The event names are case sensitive, you should probably use AddTile instead of addTile on the server side too.

Categories

Resources