Problems with Javascript Recursion - javascript

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;
}

Related

how do i check array number form "pages" to "endingPages" if they match return true else is false

when prompted i enter the number then it dont go thru the function it just makes current page = whatever i put when i get promted
INSTRUCTIONS :
Create a while loop, while(currentPage !== null)
Inside of this loop, do the following:
Detemine if the currentPage is an ending
Create a function to do this! That will keep your code nice and clean.
Have a single parameter for your function, currentPage.
Use a loop to check if the current page matches any of the page numbers in endingPages
If you find the page within endingPages, return true. If you do not, return false.
Use this function in your while loop to determine if the current page is an ending.
If the page is an ending, then print that page and exit the game
Since your while loop ends if currentPage is null, assign null to the current page and then your loop will end on the next iteration.
Hint: It would probably look nice to print out something like "GAME OVER" or "The End" here...
CODE
console.log(pages[0]);
let endingPages = [4, 9, 13, 17, 19, 20];
let currentPage = 0;
let pages = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
// Your Code Here.
currentPage = prompt(" enter page :")
ending(currentPage);
function ending (currentPage){
while(currentPage !== null) {
for(i = 0 ; i <= endingPages.length ; i ++ ){
currentPage = endingPages[i]
if (endingPages[i] === pages.length){
return true
}else{
return false
}
}
}
}
I think there is a lot of misinterpretation here. Ultimately currentPage never changes because function parameters in Javasript are pass-by-value
Either way in your code I do not see an attempt to print out currentPage after the user has been prompted and the function called
Ultimately your function should still return what you want, and you only need to test whether it returns true or false, by if (ending(currentPage)) { console.log("Game Over!"); }
2 additional obvious flaws in the provided code:
The while loop inside the function is redundant, currentPage should never be null
You can use the first answer here to make a consecutive range of numbers much more easily

Function recursion Maximum call stack size exceeded

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.

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!

Javascript flood fill algorithm getting caught in an infinite loop

Hello I am trying to implement a simple flood fill type algorithm in javascript. Basically I have a 3x3 board which I represent as a 1 dimensional array. I want to append the index for every equal value that is "touching" to a separate array. So for instance this board:
[1][1][0]
[3][1][3]
[0][0][0]
Would be represented as a 1D array ie [1,1,0,3,1,3,0,0,0]. And after running the floodFill on one of the [1] it would result with an array that looks like this [4, 1, 0] because those are the indexes in the 1d array that are touching, which have the same value.
Here is the code:
var boardArray = new Array(1,1,0,3,1,3,0,0,0);
var comboArray = new Array();
function floodFill(n, diceVal) {
if(boardArray[n] != diceVal) {
return;
}
comboArray.push(n);
if (n >0 && n < 8) {
// right
if(!(n%3==2)) {
floodFill(n+1, diceVal);
}
// left
if(!(n%3==0)) {
floodFill(n-1, diceVal);
}
// up
if(n>2) {
floodFill(n-3, diceVal);
}
// down
if(n<5) {
floodFill(n+3, diceVal);
}
} else {
return;
}
}
floodFill(4,1);
Can anyone tell me why this is getting stuck in an infinite loop?
In your "up" case, the first time through, you'll call floodFill(1,1);. That call, in its "down" case, will call floodFill(4,1);, which will soon call floodFill(1,1)
You're already keeping track of the matching squares - the only ones that will really cause any trouble. Just confirm that you're not checking the same square again:
function floodFill(n, diceVal) {
if(boardArray[n] != diceVal) {
return;
}
// have we been here before?
if (comboArray.indexOf(n) >= 0)
return;
comboArray.push(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