Recursive function (maze solver) - can't find a bug;((( - javascript

I am studying javascript and all is pretty easy to me except for some things like recursive functions. I do understand the way they work but while working with an example, I realized I can't capture the bug that prevents it from functioning...
I have an array (map) below (0 is a closed cell, 1 means path is open) and the recursive function I am trying to use to "find" path out of this "maze" by going from its top left cell to the bottom-right one.. Basically just make the function to "find" this path of 1s. But it fails;(
var map = [
[1,1,0,0],
[0,1,1,0],
[0,0,1,0],
[0,0,1,1]
]
function findpath(x,y) {
if (x<0 || x>3 || y<0 || y>3) return false; //if it is outside of map
if (x==3 && y==3) return true; // if it is the goal (exit point)
if (map[y][x]==0) return false; //it is not open
map[y][x]=9; //here marking x,y position as part of solution path outlined by "9"
if (findpath(x,y-1) == true) return true;
if (findpath(x+1,y) == true) return true;
if (findpath(x,y+1) == true) return true;
if (findpath(x-1,y) == true) return true;
map[y][x]=8; //unmark x,y as part of solution path outlined by "8"
return false;
};
findpath(0,0);

The description of "it fails" is rarely, if ever, a useful error report.
In order for someone to help you, they need much more detail than that.
In this case, the import details came out of the JavaScript error console. You should always include any error messages in your question.
However, since your code was quite short I was able to cut-and-paste it into my console where I got the message:
RangeError: Maximum call stack size exceeded
This means that your function is recursing too deeply. You either
Have bad logic in your puzzle and you are recursing into the same values over and over again
The puzzle is too complicated and you can't solve it recursively like that.
You need to add console.log statements and observe what the code is doing and see why it is going so deep.
If it is a logic error, fix the logic error. (Hint: I'm pretty sure it is -- you never mark on the map where you've been so it freely goes back and forth and back and forth over the same spot).
If it isn't, then you need to use some more advanced trick to work around the recursion, such as using a generator function and storing the changes you do in the map separately.

Quick answer:
Its locking in a loop because the order of the checks.
Start from 0:0 then try 0:1. Then from 0:1 --"Ummm... 0:0 looks promising. Let's go there". So go back to 0:0... so it locks...
Try leaving backtracking last :
if(findpath(x+1,y)) return true;
if(findpath(x,y+1)) return true;
if(findpath(x,y-1)) return true;
if(findpath(x-1,y)) return true;
This get you out of the lock just by swapping the issue around. If you start from 3:3 trying to reach 0:0 you'll be locked again.
Whats missing its a way to mark visited squares.
I think you are trying to implement an a* algorithm
UPDATE:
Here is your idea working. Just added the backtracking checks you almost implemented.
<html>
<head>
</head>
<body>
<script>
var map = [
[1,1,0,0],
[0,1,1,0],
[1,1,1,0],
[1,0,1,1]
]
var goalx = 0;
var goaly = 3;
console.log();
function findpath(x,y) {
// illegal move check
if (x < 0 || x > (map[0].length -1) || y < 0 || y > (map.length - 1)) return false; //if it is outside of map
if (map[y][x]==0) return false; //it is not open
// end move check
if (x== goalx && y== goaly){
console.log('Reached goal at: ' + x + ':' + y);
return true; // if it is the goal (exit point)
}
if(map[y][x] == 9 || map[y][x] == 8)
return false;
console.log('Im here at: ' + x + ':' + y);
map[y][x]=9; //here marking x,y position as part of solution path outlined by "9"
if(findpath(x+1,y))
return true;
if(findpath(x,y+1))
return true;
if(findpath(x,y-1))
return true;
if(findpath(x-1,y))
return true;
map[y][x]=8; //unmark x,y as part of solution path outlined by "8"
return false;
};
findpath(3, 3);
</script>
</body>
</html>

Related

Refactoring JavaScript function results

I'm working on a project using Leaflet's Mapping API. Right now, I'm cleaning up my code and there's one section I feel could be made better.
I have it set when the location is found, the location is checked both in accuracy and in bounds:
function checkLocation(position)
{
if(position.accuracy > 100)
{
return 4;
}
else if((position.latlng.lat <= **bound** || position.latlng.lat >= **bound**) || (position.latlng.lng >= **bound** || position.latlng.lng <= **bound**))
{
return 5;
}
return 0;
}
Basically, if the accuracy is too low, I throw an error with error code 4. If the coordinates are outside of where I want them to be, I throw an error with error code 5 (These are to correspond to Leaflet's builtin error codes 1-3 so I can switch on them later). I return 0 just to say there wasn't an error.
This function is called by the locationFound event, which has the following code:
var temp = checkLocation(position);
if(temp != 0)
{
// Handle error code
return;
}
This also works, but I'm not fond of how this looks. What I want is for this bit to only take like two to three lines, preferably without an if statement. I originally had the code for checkLocation in this section, but I thought having it on its own would make for cleaner and more reader-friendly code.
My question is is there any way to improve this bit? I looked into lambda expressions but didn't think it fit and I tried using a Promise, but at that point, I was losing lines trying to cut down on lines. I don't want to code golf the code, but I'm still pretty new to JavaScript and I don't know if there's any way to simplify this while still looking professional. I'm also up for changing the checkLocation function if it means improving the code.
If you refactor to this:
function invalidAccuracy(position) {
return position.accuracy > 100;
}
function outOfBounds(position) {
return (position.latlng.lat <= **bound** || position.latlng.lat >= **bound**) || (position.latlng.lng >= **bound** || position.latlng.lng <= **bound**);
}
You can handle it like this:
function checkLocation(position) {
if(invalidAccuracy(position))
return 4;
if(outOfBounds(position))
return 5;
return 0;
}
You can (if you want) put it in 1 line then:
return invalidAccuracy(position) ? 4:
outOfBounds(position) ? 5 :
0;

javascript reading an object as parameter of a function

Im making a kind of template for all usual mouse events in canvas.
so we have a class to define words, and all events must be launched when mouse is over the word.
then I´d declared a test object (pal) in the "universal" ( but now enclosed in a spacename) part of the code.
the function contains(pal)uses this test object declared as parameter, however the console saids "TypeError: Cannot read property 'posX' of undefined at contains"
as posX is an object "pal" atribute, the function contains(pal) cannot read this object I gess.
the question is ... why?
see the complete code at https://jsfiddle.net/evy182bp/
Looking at your code the line 141 is the one that causes the issue
console.log( "pincha dentro = ", contains());
Issue is that you call the contains method without passing a parameter of pal object, meaning that it automatically passes 'undefined' (take a look here)
function contains(pal) {
if((pal.posX <= mouse.x)&&
(pal.posX + pal.largoPalabra >= mouse.x)&&
(pal.posY <= mouse.y)&&
(pal.posY + pal.altoPalabra>= mouse.y))
{return true;}
else { return false;}
// if ((100 <= mouse.x)&&
// (100 + 150 >= mouse.x)&&
// (200 <= mouse.y)&&
// (200 + 40>= mouse.y)) // this works
}
As soon as you remove that line, you are without errors.
Hope this helps

Prevent touching corners (JS Game)

How can I prevent this map generator from creating touching corners like this:
-X
X-
Or
X-
-X
Here is a simplified example of the generator: http://jsfiddle.net/fDv9C/2/
Your question answers itself, almost.
Here's the fiddle: http://jsfiddle.net/qBJVY/
if (!!grid[y][x] && !!grid[y+1][x+1] && !grid[y+1][x] && !grid[y][x+1]) {
good=false;
grid[y+1][x]=2;
}
It simply checks for the combinations you do not want and patches them up. It always adds a grid point so as not to disconnect any parts of the map.
This in turn may lead to another situation where the issue may occur, but if it changed anything (that is, if it found a problem), it will simply check again. This can be optimized, for instance by recursively adjusting whatever was changed, but usually it only needs 1 or 2 passes. There's a limiter on there to not allow more than 100 passes, just in case there is some unforeseen circumstance in which it cannot fix it (I can't think of such a situation, though :) ).
Because of the way that you are creating board it's very difficulty to do this checking during generation. I create simple function that check board after. It's using flood algorithm. Here is the fiddle http://jsfiddle.net/jzTEX/8/ (blue background is original map, red background is map after checking)
Basically we create second array grid2. After filling grid we run recursively floodV function
function floodV(x,y) {
var shiftArray = [[0,1],[0,-1],[1,0],[-1,0]];
grid2[y][x]=1;
for(var k=0;k<4;k++) {
var x1=x+shiftArray[k][0];
var y1=y+shiftArray[k][1];
if(grid[y1][x1] == 1 && grid2[y1][x1] == 0 && checkV(x1,y1)) {
grid2[y1][x1] = 1;
floodV(x1,y1);
}
}
}
with the check function
function checkV(x,y) {
var checkVarr = [[-1,-1], [-1,1], [1,1], [1,-1]];
for(var k=0;k<4;k++) {
if(grid[y+checkVarr[k][0]][x+checkVarr[k][1]] == 1 && grid[y+checkVarr[k][0]][x] == 0 && grid[y][x+checkVarr[k][1]] == 0 && grid2[y+checkVarr[k][0]][x+checkVarr[k][1]] == 1)
return false;
}
return true;
}
This isn't perfect because we can sometimes throw away big parts of the map but if we try to start adding new elements we have to check whole map again (in worths case).
This is what I did: http://jsfiddle.net/fDv9C/13/
Where's the magic happening? Scroll down to lines 53 through 58:
var bottom = y_next + 1;
var left = x_next - 1;
var right = x_next + 1;
var top = y_next - 1;
if (grid[top][left] || grid[top][right] ||
grid[bottom][left] || grid[bottom][right]) continue;
In short your touching corner points can only occur at the computed next position. Hence if any one of the four corner neighbors of the next position exists, you must compute another next position.
You may even decrement the counter i when this happens to get as many paths as possible (although it doesn't really make a big difference):
var bottom = y_next + 1;
var left = x_next - 1;
var right = x_next + 1;
var top = y_next - 1;
if (grid[top][left] || grid[top][right] ||
grid[bottom][left] || grid[bottom][right]) {
i--;
continue;
}
See the demo here: http://jsfiddle.net/fDv9C/12/
Edit: I couldn't resist. I had to create an automatic map generator so that I needn't keep clicking run: http://jsfiddle.net/fDv9C/14/

jQuery object info retrieval issue [duplicate]

So I'm working with the Handsontable jQuery plugin for a project at the moment, and I've written some custom functions to work with it.
The function I'm currently having trouble with is one I've written to return the currently selected cell(when the user has only selected one, not multiple, and yes that is checked for).
Here is my code:
function getCurrentCell(){
var selection = $('div.current');
var left = selection.offset().left;
var right = left + selection.width();
var top = selection.offset().top;
var bottom = top + selection.height();
$('div.active').find('table').find('td').each(function(){
if($(this).offset().left >= left && $(this).offset().left <= right && $(this).offset().top >= top && $(this).offset().top <= bottom){
return this;
}
});
return false;
}
However, whenever I call the function such as:
var cell = getCurrentCell();
And then attempt to alert(cell) or console.log(cell), I get a false return value.
My initial thought would be that somehow the coordinates would be off, and therefore no element would be found matching the criteria, so I attempted to check by adding...
$(this).css('background-color', 'black');
...right before the return this line. That way, if the right table cell is found, it will show up on screen before actually returning in code. Funny thing is, the correct cell always has its background color changed properly. So, this function is finding the correct cell, and it is executing the code within the if loop, but when I try and capture the return value into a variable, that variable is always false.
Any help would be great! Thanks SO!
You are using .each() with a function
.each(function(){
...
return this;
});
return false;
This will return from the callback (and maybe stop the each-loop if this was false), but never break out and return from the outer getCurrentCell function! So, that one will always return false.
Quick fix:
var result = false;
<...>.each(function(){
if (<condition>) {
result = <found element>;
return false; // break each-loop
}
});
return result; // still false if nothing found
Currently there is a better way to get currently selected in Handsontable. Just use the following methods from Handsontable:
handsontable('getSelected') - Returns index of the currently selected cells
as an array [topLeftRow, topLeftCol, bottomRightRow, bottomRightCol]
handsontable('getCell', row, col) - Return element for given row,col
All methods are described here: https://github.com/warpech/jquery-handsontable

javascript object that keep accurate score in a ten-pin bowling match for a single player

recently I got a job interview for a web developer position.
They ask me to create a javascript object that keep accurate score in
a ten-pin bowling match for a single player.
I have accomplished my test.
You can run the script at this url http://jsbin.com/arele5,
If you want to edit just go to this link http://jsbin.com/arele5/edit
I have two questions:
1) The javascript object works on google chrome but jsbin complains about the following construction
var bowlingGame = new function(){...};
If I correct the construction in the following way
var bowlingGame = function(){...};
the jsbin says to me that all things are ok, but it doesn't work in google chrome. Any ideas?
2) I didn't pass the job interview, indeed I didn't receive any response; so my second question is:
what would be the best way to solve the exercise?
Thanks,
Antonio
For #1, try:
var bowlingGame = new (function(){ ...})();
As for #2, I can't exactly say why you didn't get a job, but the spacing of your code, if nothing else, isn't very pretty to look at. I just wrote this, it's much more compact and easy to read:
function game(){
this.balls = [];
}
game.prototype = {
addBalls : function(n){
this.balls.push.apply(this.balls, arguments);
},
getScoreArray : function(){
var score=0, ball=0, frame=1, frames=[], cur;
for(var i=0, l=this.balls.length; i<l; i++){
score += (cur = this.balls[i])
+ ((!ball && frame<10 && cur==10 && this.balls[i+2]) || 0)
+ (frame<10 && (((ball ? this.balls[i-1] : 0) + cur) == 10) ? this.balls[i+1] || 0 : 0);
ball = ball || (cur==10) ? 0 : 1;
ball || (frame<10 ? frames.push(score) && frame++ : frames[9] = score);
}
return frames;
}
};
If nothing else, run your code through something like: http://jsbeautifier.org/ if you can't keep indentation clean...

Categories