4 Replies - 943 Views - Last Post: 12 June 2012 - 07:16 PM

Recursive function not working as expected

Posted 09 June 2012 - 01:12 AM

Hello, I'm using a recursive function (must admit I'm a novice in that area) to indicate whose turn it is (between a player and the computer) in a simple card game im developing. Before the function is set into motion,
a variable is defined outside the function called play first, which can either be fed the value, "me" or "computer". Now depending on which the player choses, an alert box indicates
who plays first. If it is the player, it reads "your turn", waits for a prompt from player (a click in some designated container mimicks that for now), at which point, the value for play_first variable
is changed to computer and the function is run recursively. Now since it is the computer's turn, a fucntion is executed (which i haven't coded yet), and soon after that, the play_first value is
again changed to "me" indicating the player's turn and the cycle continues. The point of exit is when either player or computer has no more cards.
It seems to work fine except for the fact that, when I prompt the player to play, I dont just get a simple "computer's turn" followed by a "your turn" alert, but also all the previous "your turn" and
"computer turn" alerts that had preceeded this particular prompt, depending on the number of times the prompt has been clicked. So for example, if it is the second round for player to play and I click on the prompt, it would
alert "computer's turn" followed by "Your turn" twice, instead of just once as I expect it to. Any ideas??

var play_cards = "me" //Could also be "computer"
function whose_turn(who){
if( (p1cards.length = 0)||(p2cards.length = 0) ){//If either player is out of cards
alert("Game Over!");
}else{//If neither player is done, the game should continue
if(who == "me"){//If it is player's turn
alert("Your turn");
//Mimick the player playing a card by clicking in some div
$("#player2_container").click(function(){
//Execute player 2 play function...
//Run the whose turn function recursively, this time handing the baton to the computer
play_first = "computer";
whose_turn(play_first); //It's now computer's turn
});
}else{//If it's computer's turn
alert("Computer's turn");
//Prevent player2 from playing if he attempts...
//Execute the computer play function...
//Run the whose turn function recursively, this time handing the baton to the player
play_first = "me";
whose_turn(play_first); //It's now your turn
}//End else if it's computer's turn
}//End of else if neither player is done

Replies To: Recursive function not working as expected

Re: Recursive function not working as expected

Posted 09 June 2012 - 02:19 AM

Hey.

The problem there is that each time the who == "me" side of the code is triggered, you bind a new function callback to the #player2_container click event. So the second time the player's turn is up, and he clicks the area, both the first and second click callbacks are triggered. The third time, both the first two and the new, third, callback are called.

What you need to do is either remove the callback function from the event every time it's called, or keep it there permanently and take steps so that it will ignore clicks unless it's the players turn. - The first one is simpler, but the second one is more efficient. (Not that the efficiency will be much of an issue in a game like this.)

The first suggestion would look something like this:

// The function that will be executed on click
var onclickCallback = function() {
// Remove this event from the target element
$("#targetElem").unbind("click", onclickCallback);
// Do whatever you want to do in this callback.
console.log("Clicked!");
}
// Assign the callback to the click event.
$("#targetElem").bind("click", onclickCallback);

You see, the callback is being removed as soon as it executes, preventing it from being executed again.

Re: Recursive function not working as expected

Posted 09 June 2012 - 12:49 PM

Atli, on 09 June 2012 - 02:19 AM, said:

Hey.

The problem there is that each time the who == "me" side of the code is triggered, you bind a new function callback to the #player2_container click event. So the second time the player's turn is up, and he clicks the area, both the first and second click callbacks are triggered. The third time, both the first two and the new, third, callback are called.

What you need to do is either remove the callback function from the event every time it's called, or keep it there permanently and take steps so that it will ignore clicks unless it's the players turn. - The first one is simpler, but the second one is more efficient. (Not that the efficiency will be much of an issue in a game like this.)

The first suggestion would look something like this:

// The function that will be executed on click
var onclickCallback = function() {
// Remove this event from the target element
$("#targetElem").unbind("click", onclickCallback);
// Do whatever you want to do in this callback.
console.log("Clicked!");
}
// Assign the callback to the click event.
$("#targetElem").bind("click", onclickCallback);

You see, the callback is being removed as soon as it executes, preventing it from being executed again.

@atli, I'm not sure why all the previous callback will be trigered, it just hardly makes any sense to me, please can you enlighten me a little more? And I'm sure your solutions will work, I haven't tried them yet because I tried another simpler solution which works(completely removing the click fucntion from and placing it outside the whose_turn function)

Re: Recursive function not working as expected

@atli, I'm not sure why all the previous callback will be trigered, it just hardly makes any sense to me, please can you enlighten me a little more?

Sure. It's because when you add a callback to an event, you are adding it permanently. It won't be executed once and then removed, not unless you remove it yourself like I demonstrated in my last post. So, you see, each time you called whose_turn with a "me" value, it added a callback to the "click" event of the element. That callback will then be executed when the element is clicked, right after all the other callbacks you've added to it before.

You see there that three functions have been added to the "click" event of #targetElem. Now each time the element is clicked, all three functions will be executed and you get all three messages logged in the developer console. - These three callbacks will remain attached to the click event until they are removed.

You see there, that each time you call the function with a "me" value for who, a new function callback is added to the "click" event, so that when #targetElem is called, it and ALL the previous functions this function has added to the "click" event will be called, causing a chain-reaction. - Each time the element is clicked, it doubles the amount of function callbacks that are set on the element's click event, so that the next time it's called you'll get twice as many messages in the developer log as you did on the previous click.

It's always a danger, when dealing with recursive functions, that you'll accidentally cause an infinite loop. In this case it's not really infinite, but rather causes an exponential growth of the number of callbacks you'll cause, eventually leading to an overflow.

drayarms, on 09 June 2012 - 07:49 PM, said:

And I'm sure your solutions will work, I haven't tried them yet because I tried another simpler solution which works(completely removing the click fucntion from and placing it outside the whose_turn function)

That sounds similar to the second method I suggested. It solves the above problem because you aren't adding callbacks inside the recursive function, so that only one callback will actually be registered. I'm guessing you had to add a variable somewhere to indicate who's turn it is, so clicking the element while it's not your turn won't work? - Could you show us the working code, just so we can see how you solved it?

Re: Recursive function not working as expected

@atli, I'm not sure why all the previous callback will be trigered, it just hardly makes any sense to me, please can you enlighten me a little more?

Sure. It's because when you add a callback to an event, you are adding it permanently. It won't be executed once and then removed, not unless you remove it yourself like I demonstrated in my last post. So, you see, each time you called whose_turn with a "me" value, it added a callback to the "click" event of the element. That callback will then be executed when the element is clicked, right after all the other callbacks you've added to it before.

You see there that three functions have been added to the "click" event of #targetElem. Now each time the element is clicked, all three functions will be executed and you get all three messages logged in the developer console. - These three callbacks will remain attached to the click event until they are removed.

You see there, that each time you call the function with a "me" value for who, a new function callback is added to the "click" event, so that when #targetElem is called, it and ALL the previous functions this function has added to the "click" event will be called, causing a chain-reaction. - Each time the element is clicked, it doubles the amount of function callbacks that are set on the element's click event, so that the next time it's called you'll get twice as many messages in the developer log as you did on the previous click.

It's always a danger, when dealing with recursive functions, that you'll accidentally cause an infinite loop. In this case it's not really infinite, but rather causes an exponential growth of the number of callbacks you'll cause, eventually leading to an overflow.

drayarms, on 09 June 2012 - 07:49 PM, said:

And I'm sure your solutions will work, I haven't tried them yet because I tried another simpler solution which works(completely removing the click fucntion from and placing it outside the whose_turn function)

That sounds similar to the second method I suggested. It solves the above problem because you aren't adding callbacks inside the recursive function, so that only one callback will actually be registered. I'm guessing you had to add a variable somewhere to indicate who's turn it is, so clicking the element while it's not your turn won't work? - Could you show us the working code, just so we can see how you solved it?

@alti, The variable is declared before the function is even defined, and then within the function, the value of the variable is switched back and forth. Well here is the working code. It's just really a skeleton of what I have in mind, but works so far the way I want it.

var play_first = $("#whos_turn").val(); //Define who plays first
//Run the player play function if he plays a card
$("#player2_container").click(function(){
//Execute player 2 play function
//Run the whose turn function recursively, this time handing the baton to the computer
play_first = "computer";
whose_turn(play_first); //It's now computer's turn
});
//Define the whose turn is it recursive function
function whose_turn(who){
if( (p1cards.length = 0)||(p2cards.length = 0) ){//If either player is out of cards
alert("Game!");
}else{//If neither player is done, the game should continue
if(who == "me"){//If it is player's turn
$("#player1_container").append("<span style = 'color:green'> Your Turn </span>");
}else{//If it's computer's turn
$("#player1_container").append("<span style = 'color:red'> Computer's Turn </span>");
//Prevent player2 from playing if he attempts
//Execute the computer play function
//Run the whose turn function recursively, this time handing the baton to the player
play_first = "me";
whose_turn(play_first); //It's now your turn
}//End else if it's computer's turn
}//End of else if neither player is done
}//End of whose turn function
//Run the whose turn function feeding it the play first argument
whose_turn(play_first);