Javascript flood fill algorithm getting caught in an infinite loop - javascript

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);
// ...
}

Related

This while loop for my sorting algorithm doesn't seem to exit, why?

I am coding in javascript and am trying to make a sorting algorithm, and my while loop doesnt seem to exit, anybody know why?
let sort = true
let length = 0
let i = 1
function multiSort(n) {
length = n.length-1
while (sort=true) {
sort = false
if (n[i]>n[i+1]) {
[n[i],n[i+1] = n[i+1],n[i]]
i += 1
sort=true
if (i = length) {
i = 1
}
console.log(i)
}
}
return n
}
console.log("Final Product: ", multiSort([3,2,5,1,4]), " Iterations: ", i)
You have a couple problems here.
As mentioned your using javascript incorrectly. You should spend some time learning about how operators and comparisons work. You can read about them here. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#assignment_operators
Your while statement will never be false.
n[i+1] in your compare statement can be undefined so will be false when it shouldn't be checked.
I have re-written your algorithm using a for loop instead of a while loop since we get a free iterator when we set up the loop.
// We keep track of the iterations outside of the function
let iterations = 0
function multiSort(n) {
iterations++
// We set sort to false, if our conditions are not met we won't need to keep sorting
let sort = false
// Loop through every item in the array
for(let i = 0; i < n.length; i++) {
let next = n[i+1]
// If the current item is larger than the next swap their positions. We also have to check for undefined here since the end of the array can't compare to anything.
if(n[i] > next && next !== undefined) {
// Set sort to true since at least one item need to be sorted. This will not be set if all items in the array are in sequence.
sort = true
n[i+1] = n[i]
n[i] = next
}
}
// If we set sort to true re-run everything with the current array.
if(sort) {
multiSort(n)
}
// return the final array
return n
}
console.log("Final Product: ", multiSort([3,2,5,1,4]), " Iterations: ", iterations)

Hackerrank: Frequency Queries Timeout

I know that there are plenty of similar questions on SO on this specific thing, but I have a solution that works for all test cases EXCEPT for one (it gets timed out). Is there anyway I can make my code run faster or more efficiently... or do I need to start all over?
My logic:
I create three arrays.
Whenever there is a new value, I add it to my data array. At the same time, I add a "1" to my frequency array. The positions should be the same.
Whenever it is the same value, I simply increase the frequency value for the corresponding value by 1.
Whenever I need to return a value to say whether or not my array has a value with frequency "_", I just indexOf my frequency and tada if it's there I return 0, else I return 1.
function freqQuery(queries) {
var answer = new Array(),
data = new Array(),
frequency = new Array();
for (var i = 0; i < queries.length; i++){
var test = queries[i][0];
if (test == 1) { // first test
var place = data.indexOf(queries[i][1]);
if (place == -1){
data.push(queries[i][1]);
frequency.push(1);
} else {
frequency[place]++;
}
} else if (test == 2) { // second test
var place = data.indexOf(queries[i][1]);
if ((place != -1) && (frequency[place] > 0)) {
frequency[place]--;
}
} else if (test == 3) { // third test
if (frequency.indexOf(queries[i][1]) == -1) {
answer.push(0);
} else {
answer.push(1);
}
}
}
return answer;
}
Link: Hackerrank
Task is in category "Dictionaries and Hashmaps". Instead of data and frequency arrays create object with keys being data values and keys being frequencies. Instead of using indexOf which is O(n) you would be doing frequenciesMap[queries[i][1]] which is O(1).

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 delete loop

I have a school assignment and I have an issue with deleting something from an array of indexes.
This is what I currently have.
Function that returns array length:
function DolzinaPolja(polje){
return polje.length-1;
}
new constructor
function oseba(ime, priimek, stranka) {
this.ime=ime;
this.priimek=priimek;
this.stranka=stranka;
}
function that creates an object and pushes it into an array
function UstvariObjekt(ime,priimek, stranka) {
if (ime.length == 0 || priimek.length == 0 || stranka.length == 0) {
throw "Parametri niso popolni!";
}
else {
var novaoseba=new oseba(ime, priimek, stranka);
polje.push(novaoseba);
console.log(novaoseba.ime, novaoseba.priimek, novaoseba.stranka);
}
}
function that deletes an object from the array
function OdstraniIzPolja(x) {
if(x > polje.length - 1 || x == polje.length) {
throw"Polje ni tako veliko!";
}
for(var i=0; i<=polje.length-1;i++) {
if(x==polje[i]) {
polje.splice(x,1);
return true;
}
return false;
}
}
I am having an issue with deleting an object from the array.
Here are my tests.
var polje = [];
var x=0;
UstvariObjekt("Rene","Vucko","Stranka");
UstvariObjekt("R","V","S");
UstvariObjekt("X","Y","Z");
OdstraniIzPolja(x);
console.log(polje[0]);
console.log(polje[1]);
console.log(polje[2]);
console.log(DolzinaPolja(polje));
SO my array should be the length of 2. Since I start with 0,1,2. What I don't understand is why doesn't my function that deletes an object from the array delete the object? I've played around a little bit but often it just deletes the wrong object.
Also is the if clause for x if the length is smaller than the array length written ok?
---UPDATE----
IF I write
polje.slice(2,1) without the function just in the program, it deletes the right one. So obviously something is wrong with the loop.
This is my updated code.
function DolzinaPolja(polje){
return polje.length-=1;
}
function OdstraniIzPolja(x)
{
if(x>polje.length-1 || x==polje.length)
{
throw"Polje ni tako veliko!";
}
for(var i=polje.length-1;i>=0;i--)
{
if(x==polje[i]){
polje.splice(i,1);
return true;
}
return false;
}
}
--EDIT--
here's the code https://jsfiddle.net/2y07wtkL/
It's because you splice the object with wrong index.
If you splice using
polje.splice(x,1);
you remove the element at x index. That means when you remove first element from array, the second element from array becomes first, so the next iteration won't delete the second element you want to be deleted, but the second element from array will be deleted instead (it doesn't have to be the element you are currently iterating on).
try
function DolzinaPolja(polje){
return polje.length -= 1;
}
as in your solution you just subtract the lenght, but you dont asign it
Whenever you are deleting from a collection with indices you should do it from the end so that you are indices don't change after each delete. I think that's what is happening. Change the direction like this
for(var i=polje.length-1;i>=0;i--)
{
........
}

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