this.functionName is not a function error - javascript

I am writing a simulation for bunny survival in a meadow and have to detect the minimal plant growth rate for the bunny to survive. I decided to go with OOP. Hence, tried to design my "classes" in js. I haven't done much OOP in JS, so I am stuck. I don't understand why I keep getting "this.checkElementExists" is not a function.
I tried to follow OOP that was shown in Mozilla MDN for JS and here I am stuck. I updated to ES6 classes.
class Meadow{
constructor(){
this.grid = this.makeGrid();
//console.log(this.grid);
}
makeGrid(){
let grid = new Array(30);
for(var i=0; i < 30; i++){
grid[i] = new Array(30).fill(null);
}
return grid;
}
checkElementExists(coordinates){
if(this.grid[coordinates[0]][coordinates[1]] != null){
return true;
}else{
return false;
}
}
growAPlant(timeRate){
if(timeRate == null){
clearInterval(this.growAPlant);
}
let plant = new Plant();
let coord = plant.generateCoordinateInMeadow();
//console.log(coord);
// add a plant to the 2d array, but check if the that spot is free
// otherwise use the generateCOordinate in the meadow function
//console.log(this.grid[coord[0]][coord[1]]);
//var that = this;
var ans = checkElementExists(coord).bind(this);
console.log(ans);
while(!checkElementExists(coord)){
coord = plant.generateCoordinateInMeadow();
}
//console.log(coord);
//console.log(this.grid[coord[0]] == undefined);
this.grid[coord[0]][coord[1]] = plant;
//console.log(this.grid);
}
}
class Simulation{
constructor(){
this.passRateArray = []; // this array will be used to plot the data
this.failureRateArray = []; // array that will hold failure growth rates
this.timeToEnergyData = []; // an example would be [{0: 1000, 1: 999, 2: 998, ....., 10000: 0}]
this.rateToEnergyTimeData = {};
this.timeCounter = 100; // 10000
this.growthTimeRate = 1000; // 1 second
this.gap = 0.05;
this.meadow = new Meadow();
this.bunny = new Bunny();
}
timeToEnergyDataPopulator(currTime, energy){
var relation = {currTime : energy};
this.timeToEnergyData.push(relation);
}
simulation(){
// HERE I MAKE A CALL TO MEADOW.GROWAPLANT
setInterval(this.meadow.growAPlant.bind(this.meadow), this.growthTimeRate);
//meadow.growAPlant(this.growthTimeRate);
let bunnyMove = this.bunny.move();
// not enough energy, bunny did not survive
if(bunnyMove == false){
this.timeToEnergyDataPopulator(this.timeCounter, bunny.getBunnyEnergy());
let rate = this.growthTimeRate / 1000;
this.rateToEnergyTimeData = {rate : this.timeToEnergyData};
// add the failure rate to the failureRateArray
this.failureRateArray.push(this.growthTimeRate);
// increase the rate of plant growth
if(this.passRateArray.length < 1){
this.growthTimeRate = this.growthTimeRate + this.growthTimeRate * 0.5;
}else{
let lastSurvivalRate = this.passRateArray[this.passRateArray.length - 1];
this.growthTimeRate = lastSurvivalRate - ((lastSurvivalRate - this.growthTimeRate)*0.5);
}
// stop the meadow from growing a plant
meadow.growAPlant(null);
// stop the simulation
clearInterval(this.simulation);
}
while(!this.meadow.checkValidBunnyMove(bunnyMove).bind(this.meadow)){
bunnyMove = bunny.move();
}
if(meadow.checkIfBunnyEats(bunnyMove)){
// since bunny made still a move, -1 energy
bunny.decreaseEnergyByOne();
// and since the meadow at that coordinate had food, we add +10 to energy via eatPlant method
bunny.eatPlant();
// track the time to energy data
this.timeToEnergyDataPopulator(this.timeCounter, bunny.getBunnyEnergy);
}else{
// no food, -1 energy
bunny.decreaseEnergyByOne();
// track the time to energy data
this.timeToEnergyDataPopulator(this.timeCounter, bunny.getBunnyEnergy);
}
// decrement the timeCounter
this.timeCounter -= 1;
if(this.timeCounter <= 0){
this.timeToEnergyDataPopulator(this.timeCounter, bunny.getBunnyEnergy());
let rate = this.growthTimeRate / 1000;
this.rateToEnergyTimeData = {rate : this.timeToEnergyData};
this.passRateArray.push(this.growthTimeRate);
// bunny survived, adjust the growth rate
if(this.failureRateArray.length < 1){
this.growthTimeRate = this.growthTimeRate - (this.growthTimeRate * 0.5);
}else{
let lastFailureRate = this.failureRateArray[this.failureRateArray.length - 1];
this.growthTimeRate = this.growthTimeRate - ((this.growthTimeRate - lastFailureRate) * 0.5);
}
clearInterval(this.simulation);
}
}
runner(){
while(this.passRateArray[this.passRateArray.length - 1] - this.failureRateArray[this.failureRateArray.length - 1] > this.gap || this.passRateArray.length == 0 || this.failureRateArray.length == 0){
setInterval(this.simulation(), 1000);
}
console.log("The minimum plant regeneration rate required to sustain the bunny for 10000 units of time is " +
this.growthTimeRate + " regenerations/unit time");
}
}
Errors that I get:
1) simulation.js:62 Uncaught TypeError: this.meadow.checkValidBunnyMove is not a function
at Simulation.simulation (simulation.js:62)
at Simulation.runner (simulation.js:101)
at (index):24
2) meadow.js:1 Uncaught SyntaxError: Identifier 'Meadow' has already been declared
at VM16689 meadow.js:1
3) VM16689 meadow.js:37 Uncaught ReferenceError: checkElementExists is not defined
at Meadow.growAPlant (VM16689 meadow.js:37)
My question is why the number 1 and 3 errors persist?

clearInterval(this.growAPlant);
This clearInterval isn’t correct, because you need pass the return value of setInterval to it, not a function. It does helpfully imply that you have a setInterval(someMeadow.growAPlant, …) somewhere, though, and that’s where the issue is. When you reference a function without calling it – like when you pass it to setInterval – the object it belonged to doesn’t come with it. Then, when the timer fires, it calls the function without a this value.
In JavaScript, the value of this inside a non-arrow function is determined entirely by how the function is called, not where it’s declared. You can read about how this works in various other questions and pieces of documentation. Fixing the problem involves giving growAPlant the correct this somehow, either by:
placing a reference to it in a containing scope (i.e. moving your var that = this out one level and using that instead of this throughout)
wrapping the function in one that’ll preserve the correct value, as in
setInterval(someMeadow.growAPlant.bind(someMeadow), …);
(Function.prototype.bind) or
setInterval(function () {
someMeadow.growAPlant();
}, …);
(the someMeadow.growAPlant reference is now part of a call, so someMeadow becomes the this value for the call)
changing it into an arrow function, which doesn’t have its own this and uses the one from the containing scope
Only (2) will work when you convert to the simplest form of an ES6 class, so it’s the approach I recommend.

Explanation
First, this always refers to the first parent function. In your case it is:
this.growAPlant = function(timeRate){
//content
var that = this; // this is growAPlant
}
And
this.checkElementExists = function(coordinates){ }
Is accessible with Meadow object. However, your var that is referring to this.growAPlant = function(timeRate) not Meadow.
Solution
Create that in the beginning
function Meadow(){
var that = this;
that.growAPlant = function(timeRate){
}
that.checkElementExists = function(coordinates){
if(this.grid[coordinates[0]][coordinates[1]] != null){
return true;
}else{
return false;
}
};
return that;
}

Related

How do I get Javascript to remember a variable that is reset in my function, yet initially defined outside of my function?

I'm creating a game where the computer tries to guess the user's number based on user feedback like too high or too low. I'm using a binary search. The functions work properly, however, every time the buttons are pressed, the code resets to make the original list from 1 to 100 making the guess 50 instead of remembering the new list and guess defined inside my functions.
var list = new Array();
for (i = 0; i <= 100; i++) {
list.push(i)
}
//console.log(list)
// List is intially an empty array (list). The
// for loop generates integers from
// 0 to 100 and pushes them into the array.
var guess = list[Math.floor((list.length / 2))];
console.log(guess);
var toolow = function(guess) {
while (list.includes(guess) == true) {
list.shift()
};
var guess = list[Math.floor((list.length / 2) - 1)];
console.log(list);
console.log(guess)
}
// toolow(guess)
var toohigh = function(guess) {
var last = parseInt(list.length);
while (list.includes(guess) == true) {
list.pop()
};
var guess = list[Math.round(list.length / 2)];
console.log(list);
console.log(guess)
}
// toohigh(guess)
<h1> Guess Your Number </h1>
<button id="TooLow" onclick="toolow(guess);"> Too Low</button>
<button id="TooHigh" onclick="toohigh(guess);">Too High</button>
your over use of the variable guess is causing all sorts of issues
no need to pass guess from onclick to the function
don't declare a var guess inside the functions
et voila - your code works now
var list = new Array();
for (i = 0; i <= 100; i++) {
list.push(i)
}
//console.log(list)
// List is intially an empty array (list). The
// for loop generates integers from
// 0 to 100 and pushes them into the array.
var guess = list[Math.floor((list.length / 2))];
console.log(guess);
var toolow = function() {
while (list.includes(guess) == true) {
list.shift()
};
guess = list[Math.floor((list.length / 2) - 1)];
console.log(list);
console.log(guess)
}
// toolow(guess)
var toohigh = function() {
var last = parseInt(list.length);
while (list.includes(guess) == true) {
list.pop()
};
guess = list[Math.round(list.length / 2)];
console.log(list);
console.log(guess)
}
// toohigh(guess)
<h1> Guess Your Number </h1>
<button id="TooLow" onclick="toolow();"> Too Low</button>
<button id="TooHigh" onclick="toohigh();">Too High</button>

Set a variable to 2 but console.log says it's undefined

To give a brief overview I am creating a loading bar that needs to go from 0 - 100 based on the total num of pins dropped onto a map in comparison to how many possible locations there are. So bellow I have some logic as to how I am going about achieving that.
//pin drop counter
var pDrop = 2;
//secondary drop counter
var dropCounter = pDrop
//result length counter
var rLength;
//pin drops and then runs the check marker count function
function dropPin(){ checkMarkerCount() }
//if a new pin was dropped then we increase the size of the bar
function checkMarkerCount() {
if (dropCounter != pDrop) {
moveBar();
}
function moveBar() {
//selects the loading bar from the DOM
var barEl = document.getElementById("pro-bar");
//states how wide the bar is
var barW = 1;
//dictates how fast it moves
var barid = setInterval(frame, 10);
//gets how many pins have dropped
var counter = pDrop;
function frame(counter) {
//rLength is the length of an array = the total amount of possible pin drops
if (counter >= rLength) {
clearInterval(barid);
} else {
barW = counter;
barEl.style.width = barW + '%';
console.log(barW)
}
}
}
}
The issues is that even if I say that pDrop is equal to 2 it will log that the length is undefined.... What have I done wrong?
You have to understand the concept of variables and arguments.
function frame(i_counter) {
//rLength is the length of an array = the total amount of possible pin drops
if (i_counter >= rLength) {
clearInterval(barid);
} else {
barW = i_counter;
barEl.style.width = barW + '%';
console.log(barW)
}
}
When you call the function frame, you should set params i_counter.
You think counter you passed as argument is the same you set above.
And barW = counter on your code take the argument you set.
I changed your code a bit, so you don't get rid of counter argument.
When executing frame, do this:
setInterval(function () {
frame(2);
}, 10);

Bomberman Vanilla JS

I'm trying to make bomberman using vanilla JS, for my examination project.
I am a little stuck right now with how to take out the bombs in the array and push them into the array again after they explode.
They need to explode after 2 seconds.
My code for bombs:
function bombPlayerOne() {
let ss = new createjs.SpriteSheet(game.q.getResult('bomb'))
let temp = new createjs.Sprite(ss, "bombIt");
temp.x = playerOne.x;
temp.y = playerOne.y;
game.stage.addChild(temp);
powerUps.bombs.push(temp);
console.log("player one placed a bomb");
for (var i = powerUps.bombs.length - 1; i > 0; i--) {
powerUps.bombs.splice;
// TODO : tween bomber ud...
powerUps.bombs.push;
}
}
function bombPlayerTwo() {
let ss = new createjs.SpriteSheet(game.q.getResult('bomb'))
let temp = new createjs.Sprite(ss, "bombIt");
temp.x = playerTwo.x;
temp.y = playerTwo.y;
game.stage.addChild(temp);
powerUps.bombs.push(temp);
console.log("player two placed a bomb");
for (var i = powerUps.bombs.length - 1; i > 0; i--) {
powerUps.bombs.splice;
// TODO : tween bomber ud...
powerUps.bombs.push;
}
}
So you have a few options, and FYI this isn't necessarily a javascript question so much as how do you handle game logic/code design type of question.
1) A bomb when placed contains a reference back to it's owner. ie
bomb.owner = playerOne
2) You have a manager that controls the state of a level, which keeps track of bombs
LevelManager.player1Bombs = ....
3) You have an array of bombs placed belonging to each player, which you then update during your logic update calls.
function gameUpdate(long milliSecondsSinceLastFrame){
for(bomb in playerOne.placedBombs){
if(bomb.isExploded){
//do other cleanup
playerOne.availableBombs ++;
}
}
//... do same for player 2 etc
}
All of them have their own advantages/disadvantages.

shuffling cards with Javascript and inconsistent array values?

I'm building a little module in javascript to act like a pack of cards. My first method works but was quite simple, and so i wanted to create some shuffle methods that mimic the idea behind real world card shuffling.
Amongst some other useful functions I've create riffle, overhand and cut functions, that all seem to do there job, but when calling them repeatedly in sequence the returned pack amount is inconsistent, from running it over and over again it appears to be some sort of race condition, but can't seem to get my head around how to avoid it.
The relevant private methods are :
riffle : function riffle() {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log('top is '+top.length+" and bottom is "+bottom.length);
var hand = 'right';
var result = [];
var i = 52;
while (i > 0) {
var drop = Math.floor(Math.random()*3)+1;
var cards;
if (hand === 'right' ) {
if (drop >= top.length) {
cards = top;
} else {
cards = top.splice(0, drop);
}
hand = 'left';
} else {
if (drop >= bottom.length) {
cards = bottom;
} else {
cards = bottom.splice(0, drop);
}
hand = 'right';
}
result = result.concat(cards);
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
cut : function cut(fn) {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log(top);
Pack = bottom.concat(top);
console.log(Pack.length+" after cut");
if (fn && typeof(fn) === 'function') { fn(); }
return this;
}
Later on I have a privileged method called shuffle that calls them :
shuffle : function shuffle(cb) {
State.cardsOut = [];
Internal.generatePack().cut().riffle().riffle()
.riffle().riffle().riffle();
if (cb && typeof(cb) === 'function') { cb(); }
}
Note : I start with a generate function that creates an arrray of objects representing a full pack of 52 cards. The results I get when I console log the pack at different times after shuffles and cuts vary and I can't seem to figure out why.
you can see what i'km working on here
https://gist.github.com/Pushplaybang/66bc7a1fa5d84eee2236
Any help would be awesome.
The drop variable stores the number of cards you are supposed to be riffling from either the left or right hand. However, there are two instances:
if (drop >= top.length) {
cards = top;
}
and
if (drop >= bottom.length) {
cards = bottom;
}
where drop can be greater than the number of remaining cards in the half of the pack so more cards will be subtracted from i than you have actually riffled. You can fix this by:
if (drop >= top.length) {
drop = top.length;
cards = top;
top = [];
}
and
if (drop >= bottom.length) {
drop = top.length;
cards = bottom;
bottom = [];
}
(You need to empty the arrays or you may end up adding the same cards twice).
Other issues
You have magic numbers in the code (26 and 52) these could be constants defined in the class and given appropriate names (i.e. PACK_SIZE = 52) which would mean that if you create a sub-class representing a different number of cards then it would still work.
hand has two possible values which could be represented as a boolean but you assign it strings (again you could use constants LEFT_HAND = true, RIGHT_HAND = !LEFT_HAND).
Pack appears to be a global variable - I would have thought it ought to be a member of the class.
You do not need to name the functions as this is just polluting the global namespace: riffle : function riffle() { can just be an anonymous function riffle : function() {.
Performance - you create additional arrays with each iteration and the cards are moved multiple times. This could be more efficient.
Something like this:
PACK_SIZE: 52,
riffle : function() {
var index_of_cards_riffled_from_top = 0;
var index_of_cards_riffled_from_bottom = this.PACK_SIZE / 2;
var riffled_cards = [];
while ( index_of_cards_riffled_from_top < this.PACK_SIZE / 2
|| index_of_cards_riffled_from_bottom < this.PACK_SIZE ) {
var num_cards_to_riffle_top = Math.min( this.PACK_SIZE / 2 - index_of_cards_riffled_from_top, Math.floor( Math.random() * 3 ) + 1 );
var num_cards_to_riffle_bottom = Math.min( this.PACK_SIZE - index_of_cards_riffled_from_bottom, Math.floor( Math.random() * 3 ) + 1 );
while ( num_cards_to_riffle_top > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_top++ ] );
num_cards_to_riffle_top--;
}
while ( num_cards_to_riffle_bottom > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_bottom++ ] );
num_cards_to_riffle_bottom--;
}
}
this.Pack = riffled_cards;
}
while #MTO 's answer did solve my problem, I'd like to shed some light on how I've chosen to begin refactoring this function.
riffle : function riffle() {
var cutPos = Math.floor(Math.random()*rv)+( (cardCount-rv) / 2 );
var splitPack = {
left : Pack.splice(0, cutPos),
right : Pack.splice(0, Pack.length)
};
var hand = 'right',result = [], i = 52, cards;
while(i > 0) {
drop = Math.floor(Math.random()*3)+1;
if (drop >= splitPack[ hand ].length) {
drop = splitPack[ hand ].length;
}
cards = splitPack[ hand ].splice(0, drop);
hand = (hand === 'left') ? 'right' : 'left';
result = result.concat(cards);
cards = [];
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
a few things :
the elements that seem global are not really, as this is all wrapped within a function that creates a new "deck" object, and some elements need to be private, such as the cards remaining in the pack once dealing has begin.
While booleans would work well for the hands, I wanted to boil this down somewhat and so use the strings to select obj properties.
everything MTO said about using constants is absolutely valid.
by now splicing each time, we're removing the elements from the array.
I prefer this approach as it only uses one while loop.
lastly, this type of shuffle is meant to emulate hand shuffling, and must be combined with other hand shuffling methods, ideally in a repetitive sequence, to produce something useful,
if you want something consistently random and efficient use fischer-yates algorithm.

Can someone explain this Javascript Singleton code to me?

this file
http://www.iguanademos.com/Jare/docs/html5/Lessons/Lesson2/js/GameLoopManager.js
taken from this site
Here is the code:
// ----------------------------------------
// GameLoopManager
// By Javier Arevalo
var GameLoopManager = new function() {
this.lastTime = 0;
this.gameTick = null;
this.prevElapsed = 0;
this.prevElapsed2 = 0;
I understand the declaration of variables,
and they are used to record the time between frames.
this.run = function(gameTick) {
var prevTick = this.gameTick;
this.gameTick = gameTick;
if (this.lastTime == 0)
{
// Once started, the loop never stops.
// But this function is called to change tick functions.
// Avoid requesting multiple frames per frame.
var bindThis = this;
requestAnimationFrame(function() { bindThis.tick(); } );
this.lastTime = 0;
}
}
I don't understand why he uses var bindThis = this
this.stop = function() {
this.run(null);
}
This function set's gameTick to null, breaking the loop in this.tick function.
this.tick = function () {
if (this.gameTick != null)
{
var bindThis = this;
requestAnimationFrame(function() { bindThis.tick(); } );
}
else
{
this.lastTime = 0;
return;
}
var timeNow = Date.now();
var elapsed = timeNow - this.lastTime;
if (elapsed > 0)
{
if (this.lastTime != 0)
{
if (elapsed > 1000) // Cap max elapsed time to 1 second to avoid death spiral
elapsed = 1000;
// Hackish fps smoothing
var smoothElapsed = (elapsed + this.prevElapsed + this.prevElapsed2)/3;
this.gameTick(0.001*smoothElapsed);
this.prevElapsed2 = this.prevElapsed;
this.prevElapsed = elapsed;
}
this.lastTime = timeNow;
}
}
}
Most of this code is what I don't understand, I can see he is recording the time elapsed between frames, but the rest of the code is lost to me.
On the website he uses the term singleton, which is used to prevent the program trying to update the same frame twice?
I have a bit of experience with the javascript syntax, but the concepts of singleton, and the general goal/function of this file is lost to me.
Why is the above code needed instead of just calling
requestAnimationFrame(function() {} );
The reason he uses bindThis is that he is passing a method into an anonymous function on the next line. If he merely used this.tick(), this would be defined as the context of requestAnimationFrame. He could achieve the same thing by using call or apply.
Singletons are classes that are only instantiated once. This is a matter of practice, and not a matter of syntax - javascript doesn't know what a singleton is. By calling it a "Singleton", he is merely communicating that this is a class that is instantiated only once, and everything that needs it will reference the same instance.

Categories