Running into "Maximum call stack" errors on a recursive function - javascript

I'm making a height map editor. Basically its a grid of numbers where you can change any location by +/- 1. The editor then makes sure that there can only be a difference of 1 between any of the touching 8 tiles.
I'm doing this with a recursive function. Basically its looking at it's 8 neighbors and adjusting them as needed. If any where adjusted, call the function on all 8 neighbors.
I get Uncaught RangeError: Maximum call stack size exceeded errors after messing around for awhile and I can't see where they are coming from. I'm doing checks to make sure I don't try to access non-exsiting grid locations...
The function is this:
var moveDown = function (x, y) {
var updated = false;
if (x-1 >= 0 && Math.abs(grid[x][y] - grid[x-1][y]) > 1) {
grid[x-1][y] -= 1;
updated = true;
}
if (x-1 < size && Math.abs(grid[x][y] - grid[x+1][y]) > 1) {
grid[x+1][y] -= 1;
updated = true;
}
if (y-1 >= 0 && Math.abs(grid[x][y] - grid[x][y-1]) > 1) {
grid[x][y-1] -= 1;
updated = true;
}
if (y+1 < size && Math.abs(grid[x][y] - grid[x][y+1]) > 1) {
grid[x][y+1] -= 1;
updated = true;
}
if (x-1 >= 0 && y-1 >= 0 && Math.abs(grid[x][y] - grid[x-1][y-1]) > 1) {
grid[x-1][y-1] -= 1;
updated = true;
}
if (x-1 >= 0 && y+1 < size && Math.abs(grid[x][y] - grid[x-1][y+1]) > 1) {
grid[x-1][y+1] -= 1;
updated = true;
}
if (x+1 < size && y-1 >= 0 && Math.abs(grid[x][y] - grid[x+1][y-1]) > 1) {
grid[x+1][y-1] -= 1;
updated = true;
}
if (x+1 < size && y+1 < size && Math.abs(grid[x][y] - grid[x+1][y+1]) > 1) {
grid[x+1][y+1] -= 1;
updated = true;
}
if (updated) {
if (x-1 >= 0) { moveDown(x-1, y); }
if (x+1 < size) { moveDown(x+1, y); }
if (y-1 >= 0) { moveDown(x, y-1); }
if (y+1 < size) { moveDown(x, y+1); }
if (x-1 >= 0 && y-1 >= 0) { moveDown(x-1, y-1); }
if (x-1 >= 0 && y+1 < size) { moveDown(x-1, y+1); }
if (x+1 < size && y-1 >= 0) { moveDown(x+1, y-1); }
if (x+1 < size && y+1 < size) { moveDown(x+1, y+1); }
}
}
I have a fiddle here. And it looks like I can just inline it, so I did that too.
var size = 15
var grid;
var active = {x: -1, y: -1};
var moveDown = function (x, y) {
var updated = false;
if (x-1 >= 0 && Math.abs(grid[x][y] - grid[x-1][y]) > 1) {
grid[x-1][y] -= 1;
updated = true;
}
if (x-1 < size && Math.abs(grid[x][y] - grid[x+1][y]) > 1) {
grid[x+1][y] -= 1;
updated = true;
}
if (y-1 >= 0 && Math.abs(grid[x][y] - grid[x][y-1]) > 1) {
grid[x][y-1] -= 1;
updated = true;
}
if (y+1 < size && Math.abs(grid[x][y] - grid[x][y+1]) > 1) {
grid[x][y+1] -= 1;
updated = true;
}
if (x-1 >= 0 && y-1 >= 0 && Math.abs(grid[x][y] - grid[x-1][y-1]) > 1) {
grid[x-1][y-1] -= 1;
updated = true;
}
if (x-1 >= 0 && y+1 < size && Math.abs(grid[x][y] - grid[x-1][y+1]) > 1) {
grid[x-1][y+1] -= 1;
updated = true;
}
if (x+1 < size && y-1 >= 0 && Math.abs(grid[x][y] - grid[x+1][y-1]) > 1) {
grid[x+1][y-1] -= 1;
updated = true;
}
if (x+1 < size && y+1 < size && Math.abs(grid[x][y] - grid[x+1][y+1]) > 1) {
grid[x+1][y+1] -= 1;
updated = true;
}
if (updated) {
if (x-1 >= 0) { moveDown(x-1, y); }
if (x+1 < size) { moveDown(x+1, y); }
if (y-1 >= 0) { moveDown(x, y-1); }
if (y+1 < size) { moveDown(x, y+1); }
if (x-1 >= 0 && y-1 >= 0) { moveDown(x-1, y-1); }
if (x-1 >= 0 && y+1 < size) { moveDown(x-1, y+1); }
if (x+1 < size && y-1 >= 0) { moveDown(x+1, y-1); }
if (x+1 < size && y+1 < size) { moveDown(x+1, y+1); }
}
}
var moveUp = function (x, y) {
var updated = false;
if (x-1 >= 0 && Math.abs(grid[x][y] - grid[x-1][y]) > 1) {
grid[x-1][y] += 1;
updated = true;
}
if (x-1 < size && Math.abs(grid[x][y] - grid[x+1][y]) > 1) {
grid[x+1][y] += 1;
updated = true;
}
if (y-1 >= 0 && Math.abs(grid[x][y] - grid[x][y-1]) > 1) {
grid[x][y-1] += 1;
updated = true;
}
if (y+1 < size && Math.abs(grid[x][y] - grid[x][y+1]) > 1) {
grid[x][y+1] += 1;
updated = true;
}
if (x-1 >= 0 && y-1 >= 0 && Math.abs(grid[x][y] - grid[x-1][y-1]) > 1) {
grid[x-1][y-1] += 1;
updated = true;
}
if (x-1 >= 0 && y+1 < size && Math.abs(grid[x][y] - grid[x-1][y+1]) > 1) {
grid[x-1][y+1] += 1;
updated = true;
}
if (x+1 < size && y-1 >= 0 && Math.abs(grid[x][y] - grid[x+1][y-1]) > 1) {
grid[x+1][y-1] += 1;
updated = true;
}
if (x+1 < size && y+1 < size && Math.abs(grid[x][y] - grid[x+1][y+1]) > 1) {
grid[x+1][y+1] += 1;
updated = true;
}
if (updated) {
if (x-1 >= 0) { moveUp(x-1, y); }
if (x+1 < size) { moveUp(x+1, y); }
if (y-1 >= 0) { moveUp(x, y-1); }
if (y+1 < size) { moveUp(x, y+1); }
if (x-1 >= 0 && y-1 >= 0) { moveUp(x-1, y-1); }
if (x-1 >= 0 && y+1 < size) { moveUp(x-1, y+1); }
if (x+1 < size && y-1 >= 0) { moveUp(x+1, y-1); }
if (x+1 < size && y+1 < size) { moveUp(x+1, y+1); }
}
}
var init = function () {
$('#board').mouseleave(function () {
active.x = -1;
active.y = -1;
})
.mousemove(function () {
active.x = -1;
active.y = -1;
});
$('#reset').click(function () {
for(var x=0; x<size; x++) {
for(var y=0; y<size; y++) {
grid[x][y] = 1;
}
}
});
$(window).keydown(function (e) {
if (e.keyCode === 119 || e.keyCode === 87) {
// W
grid[active.x][active.y] += 1;
moveUp(active.x, active.y);
}
if (e.keyCode === 115 || e.keyCode === 83) {
// S
grid[active.x][active.y] -= 1;
moveDown(active.x, active.y);
}
});
grid = [];
for(var x=0; x<size; x++) {
grid[x] = [];
var row = $('<div class="row">');
for(var y=0; y<size; y++) {
grid[x][y] = 1;
var cell = $('<div id="C' + x + '_' + y + '" class="cell">');
cell.data('x', x).data('y', y);
cell.mousemove(function (e) {
var $this = $(this);
active.x = $this.data('x');
active.y = $this.data('y');
e.stopPropagation();
});
row.append(cell)
}
$('#board').append(row);
}
setInterval(function () {
for(var x=0; x<size; x++) {
for(var y=0; y<size; y++) {
$('#C' + x + '_' + y).text(grid[x][y]);
}
}
$('#info').text('x: ' + active.x + ' y: ' + active.y);
}, 100);
};
init();
#board {
padding: 20px;
z-index: -1;
}
.row {
height: 25px;
}
.cell {
position: relative;
display: inline-block;
width: 25px;
height: 25px;
border: 1px solid black;
text-align: center;
line-height: 25px;
cursor: default;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="board"></div>
<div id="info"></div>
<p>
Click on the board once to set focus. This lets the keyboard work. Press <strong>S</strong> to make a tile go down, press <strong>W</strong> to make a tile go up.
</p>
<button id="reset">Reset</button>

Uncaught RangeError: Maximum call stack size exceeded This error occurs when there is no breakpoint for recursion function. When you call any function from that function or such like that. Then the execution goes then caller function goes into stack and callee function gets into memory for execution. If there is no return statement or condition before recursion, then processor stack gets full and throws this error message.
In your case, you are calling moveDown from moveDown but there one condition which is always true and which is responsible for calling moveDown again, or same with moveUp.
Just debug with breakpoints, you will get where it is wrong.

Related

Recursion for matrix in JavaScript

I'm using recursion for finding neighbors of neighbors in a matrix (in a minesweeper game). Right now my function is finding and marking neighbors only from left till bottom right. What am I doing wrong?
Javascript:
function openAllZeroCount(elcell, i, j) {
for (var idxi = i - 1; idxi <= i + 1; idxi++) {
for (var idxj = j - 1; idxj <= j + 1; idxj++) {
if (idxi < 0 || idxi >= gSize || idxj < 0 || idxj >= gSize) continue;
if (gBoard[idxi][idxj].isMine === false && gBoard[idxi][idxj].minesAroundCount === 1) {
var elCountZero = document.getElementById('cell-' + idxi + '-' + idxj)
elCountZero.style.opacity = '0.8'
elCountZero.innerText = gBoard[idxi][idxj].minesAroundCount
}
if (gBoard[idxi][idxj].isMine === false && gBoard[idxi][idxj].minesAroundCount === 0) {
var elCountZero = document.getElementById('cell-' + idxi + '-' + idxj)
elCountZero.style.opacity = '0.8'
}
}
}
if (idxi < 0 || idxi >= gSize || idxj < 0 || idxj >= gSize) return;
if (gBoard[idxi][idxj].isMine === false) {
console.log('gboard i j:', gBoard[idxi][idxj])
openAllZeroCount(null, idxi, idxj)
}
}

Matching variables not updating

The concept is simliar to a slider. Here is the JsFiddle
Each section is set to:
visibility: hidden;
until assigned the "anim-in" class. The issue is with var $currSection and $nextSection that need the var $rightCounter to correctly evaluate.
var $currSection = $rightCounter;
var $nextSection = $rightCounter + 1;
$rightCounter is updated in the counter function:
function counter (event){
var $counterSelect = $(this).attr('id');
if ( $counterSelect == "right") {
if ( $rightCounter >= 0 && $rightCounter <= 4){
$rightCounter += 1;
console.log($rightCounter);
if ($leftCounter <= 0) {
$leftCounter = 0;
console.log($leftCounter);
}
else {
$leftCounter -= 1;
console.log($leftCounter);
}
}
}
else {
if ($leftCounter >= 0 && $leftCounter <= 4){
$leftCounter += 1;
console.log($leftCounter);
if ($rightCounter <= 0) {
$rightCounter = 0;
console.log($rightCounter);
}
else {
$rightCounter -= 1;
console.log($rightCounter);
}
}
}
animOut();
return $rightCounter;
};
The animOut function uses $currSection and $nextSection to redistribute classes, but they are not updating with the $rightCounter?
Note: the console logs are there to show what the vars are evaluating to

Betfair like Odds increment and decrement

Please help me it is very irritating. Don't know why my logic is failed every time.
I am trying to make Betfair like odds increment in my web project. Betfair have it's own price group which can be found here
LINK: https://api.developer.betfair.com/services/webapps/docs/display/1smk3cen4v3lu3yomq5qye0ni/Betfair+Price+Increments
Here is explanation:
if odds is 1.01 and some body want to increase that odds via html5 number spinner the increment will be 0.01 and if odds is 2 the increment will be 0.02. whole increment list is available in that link.
working example can be found in betfair's betslip.
here is my Javascript:
function getIncremantal(fval) {
var val = parseFloat(fval);
var step;
if (val <= 1.99) {
step = 0.01;
} else if (val > 2 && val < 3) {
step = 0.02;
} else if (val > 3 && val < 4) {
step = 0.05;
} else if (val > 4 && val < 6) {
step = 0.1;
} else if (val > 6 && val < 10) {
step = 0.2;
} else if (val > 10 && val < 19.5) {
step = 0.5;
} else if (val >= 20 && val < 30) {
step = 1;
} else if (val >= 30 && val < 50) {
step = 2;
} else if (val >= 50 && val < 100) {
step = 5;
} else if (val >= 100 && val < 1000) {
step = 10;
} else if (val > 1000) {
step = null;
}
return step;
}
Update: jsFiddle
http://jsfiddle.net/71fs0a67/1/
I tried the following which is not using number stepping, but if you use the buttons it does work. It is an alternate solution, sorry if its not what you are looking for.
HTML:
<input type="number" min="1.01" max="1000" id="num"/>
<button class="increment">+</button>
<button class="decrement">-</button>
Javascript:
$('.increment').on('click', function() {
var elem = $('#num');
var value = parseFloat(elem.val());
var result = +(value + getIncremantal(value)).toFixed(2);
elem.val(result);
});
$('.decrement').on('click', function() {
var elem = $('#num');
var value = parseFloat(elem.val());
var result = +(value - getDecremantal(value)).toFixed(2);
elem.val(result);
});
function getIncremantal(val) {
var step;
if (val < 2) {
step = 0.01;
} else if (val >= 2 && val < 3) {
step = 0.02;
} else if (val >= 3 && val < 4) {
step = 0.05;
} else if (val >= 4 && val < 6) {
step = 0.1;
} else if (val >= 6 && val < 10) {
step = 0.2;
} else if (val >= 10 && val < 20) {
step = 0.5;
} else if (val >= 20 && val < 30) {
step = 1;
} else if (val >= 30 && val < 50) {
step = 2;
} else if (val >= 50 && val < 100) {
step = 5;
} else if (val >= 100 && val < 1000) {
step = 10;
} else if (val > 1000) {
step = null;
}
return step;
}
function getDecremantal(val) {
var step;
if (val <= 2) {
step = 0.01;
} else if (val > 2 && val <= 3) {
step = 0.02;
} else if (val > 3 && val <= 4) {
step = 0.05;
} else if (val > 4 && val <= 6) {
step = 0.1;
} else if (val > 6 && val <= 10) {
step = 0.2;
} else if (val > 10 && val <= 20) {
step = 0.5;
} else if (val > 20 && val <= 30) {
step = 1;
} else if (val > 30 && val <= 50) {
step = 2;
} else if (val > 50 && val <= 100) {
step = 5;
} else if (val > 100 && val <= 1000) {
step = 10;
} else if (val > 1000) {
step = null;
}
return step;
}
http://jsfiddle.net/71fs0a67/7/
With jquery ui spinner, you can do something like this:
$( "#spinner" ).spinner({
min: 1.01,
max: 1000,
step: 0.01,
spin: function( event, ui ) {
event.preventDefault();
event.stopPropagation();
var value = this.value || ui.value;
value = parseFloat(value);
var step;
if ($(event.currentTarget).hasClass('ui-spinner-up')) {
step = getIncremantal(value);
value = +(value + step).toFixed(2);
$( "#spinner" ).spinner('value', value);
} else {
step = getDecremantal(value);
value = +(value - step).toFixed(2);
$( "#spinner" ).spinner('value', value);
}
}
});
http://jsfiddle.net/71fs0a67/9/
Your code will return undefined for whole numbers.
Change all instances of val > number to val >= number
Try this:
function getIncremantal(fval) {
var val = parseFloat(fval);
var step;
if (val < 2) {
step = 0.01;
} else if (val >= 2 && val < 3) {
step = 0.02;
} else if (val >= 3 && val < 4) {
step = 0.05;
} else if (val >= 4 && val < 6) {
step = 0.1;
} else if (val >= 6 && val < 10) {
step = 0.2;
} else if (val >= 10 && val < 20) {
step = 0.5;
} else if (val >= 20 && val < 30) {
step = 1;
} else if (val >= 30 && val < 50) {
step = 2;
} else if (val >= 50 && val < 100) {
step = 5;
} else if (val >= 100 && val < 1000) {
step = 10;
} else if (val > 1000) {
step = null;
}
return step;
}
function getDecremantal(fval) {
var val = parseFloat(fval);
var step;
if (val <= 2) {
step = 0.01;
} else if (val > 2 && val <= 3) {
step = 0.02;
} else if (val > 3 && val <= 4) {
step = 0.05;
} else if (val > 4 && val <= 6) {
step = 0.1;
} else if (val > 6 && val <= 10) {
step = 0.2;
} else if (val > 10 && val <= 20) {
step = 0.5;
} else if (val > 20 && val <= 30) {
step = 1;
} else if (val > 30 && val <= 50) {
step = 2;
} else if (val > 50 && val <= 100) {
step = 5;
} else if (val > 100 && val <= 1000) {
step = 10;
} else if (val > 1000) {
step = null;
}
return step;
}

Cannot read property of undefined caused by variables

I have a 2d array called 'map.curCellMap' storing 1's and 0's. I am using the function 'cells.getNeighbourCount' (below) to reference cells in this map.
Within the context of the 'cells.update' function (below) this is returning an undefined error. In firefox it reads 'TypeError: map.curCellMap[nextY] is undefined'. However, if I call 'cells.getNeighbourCount' with hardcoded values it does work.
What gives?
var cells = {
getNeighbourCount: function(x, y) {
/*
/* [y][x]
/* Start a top and move clockwise
*/
var neighbours = [[-1,0],[-1,1],[0,1],[1,1],[1,0],[1,-1],[0,-1]],
count = 0;
for(i = 0; i < neighbours.length; i++) {
var curNeighbour = neighbours[i];
//Skip this iteration if neighbour is out of bounds
if((y + curNeighbour[0] < 0) || (y + curNeighbour[0] > ROWS) || (x + curNeighbour[1] < 0) || (x + curNeighbour[1] > COLS)) {
continue;
}
var nextY = y+curNeighbour[0],
nextX = x+curNeighbour[1];
if(map.curCellMap[nextY][nextX] == 1) {
count++;
}
}
return count;
},
update: function() {
for(y = 0; y < ROWS; y++) {
for(x = 0; x < COLS; x++) {
var numNeighbours = this.getNeighbourCount(x, y),
newCellState;
if(numNeighbours >= 2 && numNeighbours <= 3) {
newCellState = 1;
} else {
newCellState = 0;
}
map.nextCellMap[y][x] = newCellState;
}
}
map.curCellMap = map.nextCellMap;
}
}
var map = {
curCellMap: null,
nextCellMap: null,
//Map functions
init: function() {
this.curCellMap = this.generateRandomMap(0.05);
this.nextCellMap = this.curCellMap;
},
generateRandomMap: function(density) {
/*
/* Generates a random cell map
/* density = 0.00 -> No live cells, density = 1.00 -> All cells live
*/
var map = helpers.create2DArray(ROWS, COLS);
for(y = 0; y < ROWS; y++) {
for(x = 0; x < COLS; x++) {
var rand = helpers.getRandomNum(0, 1);
map[y][x] = (rand <= density) ? 1 : 0;
}
}
return map;
}
}
The problem was
if((y + curNeighbour[0] < 0) || (y + curNeighbour[0] > ROWS) || (x + curNeighbour[1] < 0) || (x + curNeighbour[1] > COLS)) {
Needed to read
if((y + curNeighbour[0] < 0) || (y + curNeighbour[0] > ROWS-1) || (x + curNeighbour[1] < 0) || (x + curNeighbour[1] > COLS-1)) {
OOPS!

Not sure what is wrong with the function [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am completely new to this and am trying to write a program which will take inputs on a webpage and score the results in an output box. I am not sure what the problem is with this set of javascript, though I am sure that I am missing an integral piece! Any help is much appreciated!
function laten() {
var Q2 = document.getElementById('twoScore').value;
if (Q2 == "") {
Q2 = 0;}
var Q2new = 0;
if (parseInt(Q2) >= 0) && (parseInt(Q2) <= 15) {
Q2new = 0;
} else if (parseInt(Q2) > 15) && (parseInt(Q2) <=30) {
Q2new = 1;
} else if (parseInt(Q2) > 30) && (parseInt(Q2) <=60) {
Q2new = 2;
} else if (parseInt(Q2) > 60) {
Q2new = 3;
}
document.getElementById('latency').value = Q2new;
var Q5a = document.getElementById('fiveaScore').value;
if (Q5a == "") {
Q5a = 0;}
var latenAdd = parseInt(Q5a) + parseInt(Q2new);
if (latenAdd == "") {
latenAdd = 0;}
var latenScore = 0;
if (latenScore == "") {
latenScore = 0;}
if (latenAdd == 0) {
latenScore = 0;
} else if ((latenAdd >= 1) && (latenAdd <= 2)) {
latenScore = 1;
} else if ((latenAdd >= 3) && (latenAdd <= 4)) {
latenScore = 2;
} else if ((latenAdd >= 1) && (latenAdd <= 2)) {
latenScore = 3;
}
if (!isNaN(latenScore)) {
document.getElementById('latency').value = latenScore;
}
You have several syntax errors:
You are missing global parens in some if statements with multiple conditions, it should be like this:
if ( (condition 1) && (condition2))
You are also missing a final }:
Here is the final fixed code:
function laten() {
var Q2 = document.getElementById('twoScore').value;
if (Q2 == "") {
Q2 = 0;
}
var Q2new = 0;
if ((parseInt(Q2) >= 0) && (parseInt(Q2) <= 15)) {
Q2new = 0;
} else if ((parseInt(Q2) > 15) && (parseInt(Q2) <=30)) {
Q2new = 1;
} else if ((parseInt(Q2) > 30) && (parseInt(Q2) <=60)) {
Q2new = 2;
} else if (parseInt(Q2) > 60) {
Q2new = 3;
}
document.getElementById('latency').value = Q2new;
var Q5a = document.getElementById('fiveaScore').value;
if (Q5a == "") {
Q5a = 0;
}
var latenAdd = parseInt(Q5a) + parseInt(Q2new);
if (latenAdd == "") {
latenAdd = 0;
}
var latenScore = 0;
if (latenScore == "") {
latenScore = 0;
}
if (latenAdd == 0) {
latenScore = 0;
}
else if ((latenAdd >= 1) && (latenAdd <= 2)) {
latenScore = 1;
}
else if ((latenAdd >= 3) && (latenAdd <= 4)) {
latenScore = 2;
}
else if ((latenAdd >= 1) && (latenAdd <= 2)) {
latenScore = 3;
}
if (!isNaN(latenScore)) {
document.getElementById('latency').value = latenScore;
}
}

Categories