Function recursion Maximum call stack size exceeded - javascript

I am writing the Sudoku game and having trouble finding the reason for the stack overflow. In simple words, I am filling a block with numbers 1-9 randomly. But before actually putting the number in the block, I also need to check whether there is such a number in the column it belongs to and if there is, re-random the number and check again. I've been trying a function like
let columns = [];
for(i = 0; i < 9; i++){
let a = [];
columns.push(a);
}
randomCell.textContent = unusedNumbers[getRandomNumber(0, (unusedNumbers.length - 1))];
function checkColumn() {
if(columns[columnIndex - 1].includes(randomCell.textContent)) {
randomCell.textContent = unusedNumbers[getRandomNumber(0, (unusedNumbers.length - 1))];
checkColumn();
} else {
columns[columnIndex - 1].push(randomCell.textContent);
unusedNumbers.splice(unusedNumbers.indexOf(randomCell.textContent), 1);
}
}
checkColumn();
It should work just fine, and 2 of 3 times it does and I get a generated Sudoku with non-repeating numbers in both blocks and columns. But there are cases in which it loops endlessly somehow and reaches the maximum call stack size. I have also tried the same thing with the
while (1 == 1) {
if(columns[columnIndex - 1].includes(randomCell.textContent) == false) {
columns[columnIndex - 1].push(randomCell.textContent);
unusedNumbers.splice(unusedNumbers.indexOf(randomCell.textContent), 1);
break;
}
randomCell.textContent = unusedNumbers[getRandomNumber(0, (unusedNumbers.length - 1))];
}
instead of a function, but it's the same - 2 times it generates as it should, and the third time the page just freezes in an endless loop. I feel there is a small and obvious mistake in the condition which I do not see.
Edit: I get the numbers not from an RNG but from an array
let unusedNumbers = ['1','2','3','4','5','6','7','8','9'];, and after I actually pick a number after both checks, I am splicing it from the array. The RNG works with indexes here.

Related

Why does generating a random number outside of a loop, causes it to be always the same?

When I create a random number inside a while loop as a local variable everything works, but when I generate a random number as a global variable then I get stuck in an infinite loop.
I don't understand how and why this should make any difference.
Goal is to output all the random numbers that are less than 0.7 with a While Loop.
Here is the code that creates an infinite loop:
let rnd = Math.random();
let continue = true;
while (continue) {
console.log(rnd);
if (rnd > 0.7) {
continue = false;
alert(rnd + ' is bigger than 0.7!');
}
}
Here is the code that works (only thing that is changed is that the random number is generated within the while loop).
let continue = true;
while (continue) {
let rnd = Math.random();
console.log(rnd);
if (rnd > 0.7) {
continue = false;
alert(rnd + ' is bigger than 0.7!');
}
}
I'm not interested in creating this with another kind of loop, I'm just trying to understand the While Loop better.
let rnd = Math.random();
let continue = true;
while (continue) {
console.log(rnd);
if (rnd > 0.7) {
continue = false;
alert(rnd + ' is bigger than 0.7!');
}
}
Here, the random number generator will only execute one time, before the while loop. If th random number isnĀ“t > 0.7, continue would be always true and it will be an infinite loop.
However, if the random number is generated locally in the while loop, in each loop a new number will be generated so you only need to wait for a rnd number > 0.7.
It is because when you declare Math.random() as a variable it creates a binding with a random value which doesn't change. Example: 0.8.
And if it is less than or equal to 0.7 then it would result in an infinite loop because the value doesn't change with each iteration.
In the second example it works fine because it is declared locally and it's value changes with each iteration.
Hope you understood.

Trouble understanding solution for New Years Chaos on Hackerrank (Javascript)

The problem can be found here
I struggled with this for quite some time - and I've always neglected learning CS related material in Javascript or in general. But I will be interviewing at some different companies and some have notified that the tech interview will be data structure/algorithm related, so I'm currently going through some courses online and completing challenges on Hackerrank.
Anyway - I eventually caved after a few hours and looked up some solutions.
One being this:
let queue1 = [2, 1, 5, 3, 4];
let queue2 = [2, 5, 1, 3, 4];
function minimumBribes(q) {
let bribes = 0;
let currentIndex = q.length - 1;
let positionDiff;
while (currentIndex >= 0) {
const currentValue = q[currentIndex];
const valuePosition = currentIndex + 1;
positionDiff = currentValue - valuePosition;
if (positionDiff >= 3) {
return "Too chaotic";
}
const briberPosition = currentValue - 2;
for (let comparePosition = Math.max(0, briberPosition); comparePosition < currentIndex; comparePosition++) {
const compare = q[comparePosition];
if (compare > currentValue) {
bribes++;
}
}
currentIndex--;
}
return bribes;
}
minimumBribes(queue1)
I understand that we need to check if a number has moved forward more than 2 spaces, and checking its value vs position does this.
However, getting to
const briberPosition = currentValue - 2;
for (let comparePosition = Math.max(0, briberPosition); comparePosition < currentIndex; comparePosition++) { ... }
is leaving me confused. I've ran through this slowly in debugger with different values and I can't wrap my head around why we set the currentValue - 2 to the briberPosition, and then checking that vs the index.
Could someone share some light?
The final question of the challenge is "HOW MANY bribes are needed to get the queue into this state".
So he chose to go from the end on every number and first checks if its 3 or more places ahead of its original position- this part you already got.
Now, if its not 3 or more places away- you want to know HOW many places away, this can be done simply by going 2 places back, and check if the numbers 2 places away and 1 place away are greater than the value in our current position. If they are- it means they bribed him to get there so we increment the "bribes" variable and go on to the next integer and repeat the process. Since he goes at the array from the end, he decrements the position to get to the farthest place a "briber" of our current value can be.
If it helps, this whole for loop can be completely skipped when positionDiff is 0, if he checked that after the too chaotic check, it could be a little more efficient.
Notice that in the "for" loop he trims it to be the Math.max(0, briberPosition) to avoid going beyond array limit.

Identifying edge cases of a one-dimensional array in Javascript

I'm creating a 2-dimensional heat map which has functionality when you click on any pixel. It grabs data associated with the index of every pixel (including adjacent pixels) and plots it. It currently looks like this:
The problem that I'm encountering is when I click on a left or right edge pixel, since it grabs data from adjacent pixels, it can retrieve data from the opposite side of the graph since it is all within a one-dimensional array. I am trying to create a conditional which checks if the clicked pixel is an edge case, and then configures the magnified graph accordingly to not show points from the other side of the main graph. This is the code I have so far:
// pushes all dataMagnified arrays left and right of i to magMainStore
var dataGrabber = function(indexGrabbed, arrayPushed) {
// iterates through all 5 pixels being selected
for (var b = -2; b <= 2; b++) {
var divValue = toString(i / cropLength + b);
// checks if selected index exists, and if it is not in the prior row, or if it is equal to zero
if (dataMagnified[indexGrabbed + b] != undefined && (& divValue.indexOf(".")!=-1)) {
dataMagnified[indexGrabbed + b].forEach(function(z) {
arrayPushed.push(z);
})
}
}
};
I am trying to get the same result as if I had a two dimensional array, and finding when the adjacent values within a single array is undefined. This is the line where I'm creating a conditional for that
if (dataMagnified[indexGrabbed + b] != undefined && (& divValue.indexOf(".")!=-1)) {
The second condition after the and is my attempts so far trying to figure this out. I'm unsure if I can even do this within a for loop that iterates 5 times or if I have to create multiple conditions for this. In addition, here's an image displaying what I'm trying to do:
Thank you!
Your approach looks overly complex and will perform rather slowly. For example, converting numbers to strings to be able to use .indexOf() to find a decimal point just for the sake of checking for integer numbers doesn't seem right.
A much simpler and more elegant solution might be the following function which will return the selection range bounded by the limits of the row:
function getBoundedSelection(indexGrabbed, selectionWidth) {
return dataMagnified.slice(
Math.max(Math.floor(indexGrabbed/cropLength) * cropLength, indexGrabbed - selectionWidth),
Math.min(rowStartIndex + cropLength, indexGrabbed + selectionWidth)
);
}
Here, to keep it as flexible as possible, selectionWidth determines the width of the selected range to either side of indexGrabbed. This would be 2 in your case.
As an explanation of what this does, I have broken it down:
function getBoundedSelection(indexGrabbed, selectionWidth) {
// Calculate the row indexGrabbed is on.
var row = Math.floor(indexGrabbed/cropLength);
// Determine the first index on that row.
var rowStartIndex = row * cropLength;
// Get the start index of the selection range or the start of the row,
// whatever is larger.
var selStartIndex = Math.max(rowStartIndex, indexGrabbed - selectionWidth);
// Determine the last index on that row
var rowEndIndex = rowStartIndex + cropLength;
// Get the end index of the selection range or the end of the row,
//whatever is smaller.
var selEndIndex = Math.min(rowEndIndex, indexGrabbed + selectionWidth);
// Return the slice bounded by the row's limits.
return dataMagnified.slice(selStartIndex, selEndIndex);
}
So I discovered that since the results of the clicked position would create a variable start and end position in the for loop, the only way to do this was as follows:
I started the same; all the code is nested in one function:
var dataGrabber = function(indexGrabbed, arrayPushed) {
I then create a second function that takes a start and end point as arguments, then passes them as the for loop starting point and ending condition:
var magnifyCondition = function (start, end) {
for (var b = start; b <= end; b++) {
if (dataMagnified[indexGrabbed + b] != undefined) {
dataMagnified[indexGrabbed + b].forEach(function (z) {
arrayPushed.push(z);
})
}
}
};
After that, I created 5 independent conditional statements since the start and end points can't be easily iterated through:
if (((indexGrabbed - 1) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-1, 2);
}
else if ((indexGrabbed / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(0, 2);
}
else if (((indexGrabbed + 1) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-2, 0);
}
else if (((indexGrabbed + 2) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-2, 1);
}
else {
magnifyCondition(-2, 2);
}
};
Lastly, I pass the index grabbed (i of the on clicked function) and an arbitrary array where the values get stored.
dataGrabber(i, magMainStore);
If there's a better way instead of the if statements, please let me know and I'd be happy to organize it better in the future!

Problems with Javascript Recursion

I am currently trying to write a script to determine the minimum number of "jumps" you can make in an array to get back to the initial starting number. The starting number is always the biggest number in the array.
As an example if the array was [2, 3, 5, 6, 1] then the starting number would be 6 because it is the biggest. You can then jump 6 positions either left or right (you can choose) and where you end up is your next number. Lets say we went right, we would end up at 1 because we would loop back to the beginning of the array. You could then go 1 left and end up back at the 6 again. Therefore the minimum number of jumps is 2.
I wrote the below functions to take the input array and determine the minimum number of jumps, however, the recursion in the search() function is not working as I would hope / expect.
Each search function call calls itself 2 times (once for left, and once for right), however the constantly incrementing count variable gets "confused". Once one line of recursion ends (when count>arr.length) I would expect it to go back to the previous step and do the right line of recursion. But the count variable does not revert to the previous step (it stays as it is) and therefore the entire function just stops.
TL DR: How do you make a javascript functions variables maintain what they are even when the function is called again and the variables are overwritten?
function ArrayJumping(arr) {
//Find the largest number
var startingPos=arr.indexOf(Math.max.apply(null, arr));
//Find the multiple we are searching for
var multiple=arr.length;
return search(startingPos,multiple,arr,startingPos+1,0);
}
function search(pos,mult,arr,target,count) {
if (count>arr.length) {
return false;
}
var tpos=pos+1;
if ((tpos==target && count!=0) || (tpos%target==0 && count!=0)) {
return count;
}
for (var direction=-1;direction<=1;direction+=2) {
nPos=getRealPos(pos+(direction*arr[pos]),arr);
var result=search(nPos,mult,arr,target,count+1);
if (result!=false) {
return result;
}
}
}
function getRealPos(n,numArr) {
if (n>=0) {
while (n>numArr.length) {
n-=numArr.length;
}
} else {
while (n<0) {
n+=numArr.length;
}
}
return n;
}

Generating random unique data takes too long and eats 100% CPU

WARNING: CPU Usage goes to 100%, be careful.
Link to the jsFiddle
This script has been written to design a dynamic snake and ladder board. Everytime the page is refreshed a new board is created. Most of the time all of the background images do not appear, and the CPU usage goes up to 100%. But on occasion all of them appear and the CPU usage is normal.
Opera shows some of the background images, Firefox lags and asks me if I wish to stop the script.
I believe that the problem is with these lines of code:
for(var key in origin) // Need to implement check to ensure that two keys do not have the same VALUES!
{
if(origin[key] == random_1 || origin[key] == random_2 || key == random_2) // End points cannot be the same AND starting and end points cannot be the same.
{
valFlag = 1;
}
console.log(key);
}
Your algorithm is very ineffective. When array is almost filled up, you literally do millions of useless iterations until you're in luck and RNG accidentally picks missing number. Rewrite it to:
Generate an array of all possible numbers - from 1 to 99.
When you need a random numbers, generate a random index in current bounds of this array, splice element and this random position, removing it from array and use its value as your desired random number.
If generated numbers don't fit some of your conditions (minDiff?) return them back to array. Do note, that you can still stall in loop forever if everything that is left in array is unable to fit your conditions.
Every value you pull from array in this way is guaranteed to be unique, since you originally filled it with unique numbers and remove them on use.
I've stripped drawing and placed generated numbers into array that you can check in console. Put your drawing back and it should work - numbers are generated instantly now:
var snakes = ['./Images/Snakes/snake1.png','./Images/Snakes/snake2.jpg','./Images/Snakes/snake3.gif','./Images/Snakes/snake4.gif','./Images/Snakes/snake5.gif','./Images/Snakes/snake6.jpg'];
var ladders = ['./Images/Ladders/ladder1.jpg','./Images/Ladders/ladder2.jpg','./Images/Ladders/ladder3.png','./Images/Ladders/ladder4.jpg','./Images/Ladders/ladder5.png'];
function drawTable()
{
// Now generating snakes.
generateRand(snakes,0);
generateRand(ladders,1);
}
var uniqNumbers = []
for(var idx = 1; idx < 100; idx++){ uniqNumbers.push(idx) }
var results = []
function generateRand(arr,flag)
{
var valFlag = 0;
var minDiff = 8; // Minimum difference between start of snake/ladder to its end.
var temp;
for(var i = 0; i< arr.length; ++i) {
var valid = false
// This is the single place it still can hang, through with current size of arrays it is highly unlikely
do {
var random_1 = uniqNumbers.splice(Math.random() * uniqNumbers.length, 1)[0]
var random_2 = uniqNumbers.splice(Math.random() * uniqNumbers.length, 1)[0]
if (Math.abs(random_1 - random_2) < minDiff) {
// return numbers
uniqNumbers.push(random_1)
uniqNumbers.push(random_2)
} else {
valid = true
}
} while (!valid);
if(flag == 0) // Snake
{
if(random_1 < random_2) // Swapping them if the first number is smaller than the second number.
{
var temp = random_1; random_1 = random_2; random_2 = temp
}
}
else // Ladders
{
if(random_1>random_2) // Swapping them if the first number is greater than the second number.
{
var temp = random_1; random_1 = random_2; random_2 = temp
}
}
// Just for debug - look results up on console
results.push([random_1, random_2])
}
}
drawTable()
I had a problem like this using "HighCharts", in a for loop - "browsers" have an in-built functionality to detect dead scripts or infinite loops. So the browsers halts or pop-ups up a message saying not responding. Not sure if you have that symptom!
This was resulted from a "loop" with a large pool of data. I wrote a tutorial on it on CodeProject, you might try it, and it might be your answer.
http://www.codeproject.com/Tips/406739/Preventing-Stop-running-this-script-in-Browsers

Categories