Why does clearInterval(...) not seem to be doing its job here? - javascript

Repro steps:
Go to http://playclassicsnake.com/Play
Click "Fast" (or "Slow" or even "Medium") on "Set Speed" under "Controls"
Hit left arrow key and wait until snake hits left wall and "Game Over!"
Look at JavaScript console
See something like
Uncaught TypeError: Cannot read property '0' of undefined
Refresh the page
Hit left arrow key and wait until snake hits left wall and "Game Over!"
Don't see same error in the console as before
My investigation:
The reason for the error is that the function move in the first case is being called after "Game Over!" and in the second case isn't. The second case is the correct behavior. In both cases a function endGame that starts out like
this.endGame = function ( msg )
{
this.inProgress = false;
clearInterval(this.mover);
is being called, but for some reason the interval is failing to clear in the first case. It makes no sense because the only difference between what happens in the first case and second case is that in the second case the speed is set with
SG.setSpeed($('#speed-form input[type="radio"]:checked').val());
on page load to start game, and in the first case it is set with that and then updated with
$('#speed-form input[type="radio"]').click(function ( )
{
SG.setSpeed($(this).val());
});
when you click one of the radio buttons. I've tested with
this.endGame = function ( msg )
{
console.log(this.mover); // TEST
this.inProgress = false;
clearInterval(this.mover);
that in both cases this.mover is defined. For reasons I don't understand and that may have to do with my problem, in the first case the the ID of the interval is always a high number like 68, 74, etc., while in the second case it is always 2!
I have no idea what's going on here. Any help is greatly appreciated.
Full code here.

The problem was that when you were calling clearInterval(this.mover);, you were only clearing the last interval set to this.mover, and all intervals before it were being left untouched. The solution I proposed (which seems to have worked) was to clear the previous interval every time a new one is set. This takes care of that issue because there is at most one interval at any given moment.

Related

Possible to have two conditions for an IF statement in javascript?

Alright so I am currently making a PBBG based game. However, I have been having trouble with my function for a button click and two conditions in my if statement. I am actually not positive if this is at all possible, I searched everywhere I could think of but nothing gave me a definitive answer for getting this to work.
Basically, what I am trying to achieve, is that when a player presses the 'Attack' button, the player then receives the amount of experience points and gold they get from defeating that monster. Then, after that function runs, I am setting a delay of 6 seconds to where they can't press the button to attack again until the 6 seconds have passed.
I did get the function and onClick to work where when they win the fight, the game awards them the experience and gold from the kill. That all worked great and I made sure that was all working BEFORE I started adding in the time delay function and all.
Here is my code for the function with the time delay I am trying to add: Code
(Won't allow me to embed pictures yet so a link will have to do for now) and I am using just an HTML button with the onClick value set to SingleAttack(). The code with the problem appears to be in this part...
if (attackReady) || (currentExp >= NeededExp) {...}
What I have done here is I am holding a boolean, named attackReady and setting it to 'true'. When the player presses the Attack button, it then changes the 'true' value to 'false' and then adds a setTimeout() and period in miliseconds for delay. It then sets attackReady back to true and puts a 6 second time delay before the function can be called again.
You'll notice there is an if and an else if in my function. The if code runs only when a players current experience points are greater than or equal to the needed experience points. I think my problem is coming from having two conditions in the if statement. I am not entirely sure if that is possible, I have looked everywhere to find an answer and nothing for javascript specifically. I know C# allows it, does javascript allow it and if so, did I do it right or have I done it wrong?
When the Attack button is clicked and the function is called, it does nothing at all. Any insights?
As #Thomas noted, the entire test needs to also be enclosed in braces. You currently have:
if (attackReady) || (currentExp >= NeededExp) {...}
and what you want is either:
if ( (attackReady) || (currentExp >= NeededExp) ) {...}
or more simply:
if (attackReady || currentExp >= NeededExp) {...}
One more thing:
From your description, it might be the case that you want both tests to be true to execute that block of code. If that is the case you want to use an AND with && rather than the || OR test. OR is true if either the left or the right hand side is true.

Javascript recursion onEvent on Code.org

I am having trouble with a project for my AP CompSci class. We're supposed to create programs using the App Lab on code.org. The problem is every time I stop the timed loop, the resulting number gets printed more than once after the first stop. I'm pretty sure I didn't add a recursion element so it's odd. Is there something wrong with the code or is it something else?
https://studio.code.org/projects/applab/KeB7MbUhChuQbQ6HiAA7GeED7Y9trOEJJ9GXld7lOfc
Edit:
onEvent("stop3", "click", function(){
hideElement("stop3");
stopTimedLoop();
showElement("return3");
var points = Math.round(10*(15-(time/100)));
console.log(time);
score = score + points;
update();
});
The recursion happens after the first time the button is clicked. On the second click the function would rerun after completing the update. On face value the code should only run once per click, but it runs according to how many previous clicks have been made. I can't tell if its a server side problem or something wrong with the editor. The problem seems to be isolated to this function, but there is no recursion so I'm starting to wonder if there is a problem with it server side.

Whack-A-Mole game with huge bug! Can I get some help fixing it?

I am writing a Whack-A-Mole game for class using HTML5, CSS3 and JavaScript. I have run into a very interesting bug where, at seemingly random intervals, my moles with stop changing their "onBoard" variables and, as a result, will stop being assigned to the board. Something similar has also happened with the holes, but not as often in my testing. All of this is completely independent of user interaction.
You guys and gals are my absolute last hope before I scrap the project and start completely from scratch. This has frustrated me to no end. Here is the Codepen and my github if you prefer to have the images.
Since Codepen links apparently require accompanying code, here is the function where I believe the problem is occuring.
// Run the game
function run() {
var interval = (Math.floor(Math.random() * 7) * 1000);
if(firstRound) {
renderHole(mole(), hole(), lifeSpan());
firstRound = false;
}
setTimeout(function() {
renderHole(mole(), hole(), lifeSpan());
run();
}, interval);
}
What I believe is happening is this. The function runs at random intervals, between 0-6 seconds. If the function runs too quickly, the data that is passed to my renderHole() function gets overwritten with the new data, thus causing the previous hole and mole to never be taken off the board (variable wise at least).
EDIT: It turns out that my issue came from my not having returns on my recursive function calls. Having come from a different language, I was not aware that, in JavaScript, functions return "undefined" if nothing else is indicated. I am, however, marking GameAlchemist's answer as the correct one due to the fact that my original code was convoluted and confusing, as well as redundant in places. Thank you all for your help!
You have done here and there in your code some design mistakes that, one after another, makes the code hard to read and follow, and quite impossible to debug.
the mole() function might return a mole... or not... or create a timeout to call itself later.. what will be done with the result when mole calls itself again ? nothing, so it will just be marked as onBoard never to be seen again.
--->>> Have a clear definition and a single responsibility for mole(): for instance 'returns an available non-displayed mole character or null'. And that's all, no count, no marking of the objects, just KISS (Keep It Simple S...) : it should always return a value and never trigger a timeout.
Quite the same goes for hole() : return a free hole or null, no marking, no timeout set.
render should be simplified : get a mole, get a hole, if either couldn't be found bye bye. If a mole+hole was found, just setup the new mole/hole couple + event handler (in a separate function). Your main run function will ensure to try again and again to spawn moles.

Testing/validating/evaluating the outcome of every path in a function?

Disclaimer - I've tried finding an answer to this via google/stackoverflow, but I don't know how to define the problem (I don't know the proper term)
I have many small AI snippets such as what follows. There is an ._ai snippet (like below) per enemy type, with one function next() which is called by the finite state machine in the main game loop (fyi: the next function doesn't get called every update iteration, only when the enemy is shifted from the queue).
The question: How do I test every case (taking into account some enemy AI snippets might be more complex, having cases that may occur 1 in 1000 turns) and ensure the code is valid?
In the example below, if I added the line blabla/1 under count++, the error might not crop for a long time, as the Javascript interpreter won't catch the error until it hits that particular path. In compiled languages, adding garbage such as blabla/1 would be caught at compile time.
// AI Snippet
this._ai = (function(commands){
var count = 0;
return {
next: function(onDone, goodies, baddies) {
// If the internal counter reaches
// 2, launch a super attack and
// reset the count
if(count >= 2) {
commands.super(onDone);
count = 0;
}
else {
// If not performing the super attack
// there is a 50% chance of calling
// the `attack` command
if(chance(50)) {
var target = goodies[0];
commands.attack(onDone, target);
}
// Or a 50% chance of calling the
// `charge` command
else {
commands.charge(onDone);
count++;
}
}
}
};
})(this._commands);
I could rig the random generator to return a table of values from 0-n and run next 1000's of times against each number. I just don't feel like that is will concretely tell me every path is error free.
As you say, unit tests must test every path so you will be sure all works well.
But you should be able to decide which path the method will follow before calling it on your tests, so you're be able to know if the method behaviour is the expected one, and if there is any error.
So, for example, if there is a path that will be followed in only one of every 1000 executions, you shouldn't need to test all 0, 1, 2 ... 999 cases. You only one combination of results that behave distinctly.
For example, in the snippet shown you have these cases:
the counter has reached 2
the counter has not reached 2 and chance returns true
the counter has not reached 2 and chance returns false
One way to archieve this is taking control of the counter and of the chance method by mocking them.
If you want to know what happens when the counter has reached 2 and the next method is called, just pass a counter with 2 and call next. You don't need to reach 2 on the counter by really passing for all the code.
As for the randomizer, you don't need to try until the randomizer returns the value you want to test. Make it a mock and configure it to behave as you need for each case.
I hope this helps.

Canvas Game: My snake eats itself

Since my other bug got solved, I'm posting a new question for this bug.
I made a Snake canvas game, but my snake tends to eat itself when you press two buttons at the same time.. I'm not sure how to explain it properly, but this is what happens:
Say my snake is moving towards the left, and I press down + right, it'll eat itself and trigger a game over. Same when it goes to the right: down + left and bam, dead. I can't seem to reproduce the bug when the snake goes up and down though..
This is the code involved with changing directions:
bindEvents = ->
keysToDirections =
37 : LEFT
38 : UP
39 : RIGHT
40 : DOWN
$(document).keydown (e) ->
key = e.which
newDirection = keysToDirections[key]
if newDirection
setDirection newDirection
e.preventDefault()
setDirection = (newDirection) ->
# TODO: there's some bug here; try pressing two buttons at the same time..
switch Snake.direction
when UP, DOWN
allowedDirections = [LEFT, RIGHT]
when LEFT, RIGHT
allowedDirections = [UP, DOWN]
if allowedDirections.indexOf(newDirection) > -1
Snake.direction = newDirection
I thought there was something wrong with the compiled JS because my switch statement doesn't have a break on the last case; however, I tried adding else return to the coffee script file and this didn't change anything. I'm completely lost here so I hope someone will be able to spot where I'm going wrong.
It seems as if it takes the keydown events right, but they get overridden when you press too fast. If that makes sense? Like.. You'd press up when the snake is going right, but then you press left before it actually had a chance to go up and then it just goes left. Chaotic sentence right there, I suggest you play for a while and try to reproduce this if you're as intrigued as I am :(
I have no clue how to debug this properly.. The game loop tends to spam me with messages when I do console.log.
A live demo and link to my github repo can be found here
Thanks in advance.
The problem is that if you push the keys quickly enough, its possible to trigger the event callback multiple times during one frame. Thus, if the snake is going down, it can turn right and then up in the same frame, thus reversing direction and eating itself. I'll suggest two ways to solve this problem:
The first is to set a flag when the direction is changed, i.e.:
if allowedDirections.indexOf(newDirection) > -1 and !turnedThisFrame
Snake.direction = newDirection
turnedThisFrame = true
and then, in your code that runs every frame, set turnedThisFrame to false.
The second is to rework how you deal with keypresses. This is often the approach that I take. Keep a map of which keys are pressed (say, keysDown), and bind a function that sets keysDown[e.which] = true to keydown and another function which sets keysDown[e.which] = false to keyup. Then, you can check which keys are pressed in the code that runs each frame, and act accordingly.
Here's some details on how I implemented the second solution in a current project. The following code appears in my onload handler:
do ->
keysDown = []
$(document).keydown (e) ->
keysDown.push e.which
$(document).keyup (e) ->
keysDown = _.without keysDown, e.which
window.isPressed = (keyCode) -> keyCode in keysDown
The do -> construct is used to create a function and immediately calling it, which has the beneficial effect of keeping keysDown in closure for the handlers and isPressed, while avoiding polluting the main scope of the onload callback.
Then, at the beginning of my tick function (which runs once per frame and handles game logic, drawing the screen, etc.) I would have something like this:
switch Snake.direction
when UP, DOWN
allowedDirections = [LEFT, RIGHT]
when LEFT, RIGHT
allowedDirections = [UP, DOWN]
for direction in allowedDirections
if isPressed(directionToKey[direction])
Snake.direction = newDirection
break
The map directionToKey would just be the opposite of your keysToDirections.
Note that this means that keys listed first in allowedDirections will have priority, i.e. if you are going right and press both up and down in the same frame, up will occur regardless of which was pressed first.
Added advantage to this second method: you don't have to change the key handler callbacks when switching between, say, a menu screen and the game. You just have a different tick function checking for what is pressed.
You must retain the last direction is which the snake actually moved and determine allowedDirection based on that. Pressing a key only represents the intent to move in that direction, but it does not actually move when the key is pressed, but based on the speed of the game, i guess.
Your snake eating itself shows a problem with your hit detection (and hit detection handling) code. If you're hitting the snake, the game should end. A snake is not an apple! Nevermind, apparently I missed the part where the game is ending for you.
If you choose to allow pixel-granularity (I can't see your demo, my work's network is half-down..), you can't really make U turns "safe", like you could with a hex-granularity approach. Not saying your choice is bad, just telling you to pick your battles, some you just can't win.
I had the same problem, but I solved it by implementing a queue; each keypress sets the direction of the snake and that (new) direction is pushed to an array. Then, before I actually move the snake in the game loop, I check if there are any elements in my direction queue. If so, I shift it once via Array.shift. The return value is the first element of the queue and the new direction for my snake. Array.shift also removes that element from the queue, which is exactly what we want. If two keys are pressed almost simultaneously, the first and the second direction change is stored in our queue, and our aforementioned routine takes care of the rest, by first applying the first direction change in the next available tick and then applying the second direction change in the next tick thereafter.
Hope that makes sense? :-)

Categories