To make a Constructor Asynchronous and only be called once all the data from the API is retrieved into the Array - javascript

Data fetched from the API stored in the Array : movieInfo
let movieInfo = [];
async function popularMovies() {
let page = 1;
let lastResult = [];
do {
try {
const resp = await fetch(popularAPIURL + `&page=${page}`);
const data = await resp.json();
lastResult = data.total_pages;
data.results.forEach((result) => {
movieInfo.push(result);
});
page++;
} catch (err) {
console.error(`Oops, Something is wrong ${err}`);
}
} while (lastResult);
console.log(movieInfo);
}
console.time("Time my API Call");
popularMovies();
console.timeEnd("Time my API Call");
Constructor function created for HTML pagination which also renders data from the Array: movieInfo to create respective Movie cards which basically displays the movie name, image and descp. Now I want to make this Constructor Asynchronous and only be called once the Array : MovieInfo has retrieved all the data from the API
class Paginator {
constructor(totalRecords, recordsPerPage = 1, visiblePages = 1) {
this.recordsPerPage = recordsPerPage;
this.totalRecords = totalRecords;
this.noOfPages = Math.ceil(this.totalRecords / this.recordsPerPage);
this.visiblePages = visiblePages;
this.activePage = 1;
this.visiblePagesEndRange = visiblePages;
this.validate();
}
validate() {
if (this.recordsPerPage <= 0) {
this.recordsPerPage = 1;
}
if (this.visiblePages <= 0) {
this.visiblePages = 1;
}
if (this.totalRecords <= 0) {
this.totalRecords = 1;
}
if (this.noOfPages <= 0) {
this.noOfPages = Math.ceil(this.totalRecords / this.recordsPerPage);
}
if (this.visiblePagesEndRange <= 0) {
this.visiblePagesEndRange = this.visiblePages;
}
if (this.visiblePages > this.noOfPages) {
this.visiblePages = this.noOfPages;
this.visiblePagesEndRange = this.visiblePages;
}
if (this.recordsPerPage > this.totalRecords) {
this.recordsPerPage = this.totalRecords;
}
}
gotoNextPage() {
if (this.activePage < this.noOfPages) {
this.activePage += 1;
if (this.activePage > this.visiblePagesEndRange) {
this.visiblePagesEndRange += this.visiblePages;
this.visiblePagesEndRange = Math.min(
this.visiblePagesEndRange,
this.noOfPages
);
}
}
}
gotoPrevPage() {
if (this.activePage > 1) {
this.activePage -= 1;
if (this.activePage % this.visiblePages === 0) {
this.visiblePagesEndRange = this.activePage;
}
}
}
gotoFirstPage() {
this.activePage = 1;
this.visiblePagesEndRange = this.visiblePages;
}
gotoLastPage() {
this.activePage = this.noOfPages;
this.visiblePagesEndRange = this.noOfPages;
}
gotoPage(page) {
this.activePage = page;
}
getVisiblePagesRange() {
let beginningVisiblePage;
let endingVisiblePage;
// When the visiblepagesendrange % visiblepages is not zero (which means that all the pages cannot be fit in the visible pages range) and if our ending page range is equal to total no pages then the beginning would be equivalent to visble page range - ((visible page range mod visiblepage range) - 1) i.e the leftover pages until the end.
if (
this.visiblePagesEndRange % this.visiblePages !== 0 &&
this.visiblePagesEndRange === this.noOfPages
) {
beginningVisiblePage =
this.visiblePagesEndRange -
((this.visiblePagesEndRange % this.visiblePages) - 1);
}
// else we are always in a place where, current visible page end range - visible page range + 1 will return us the correct beginning position for the page range.
else {
beginningVisiblePage = this.visiblePagesEndRange - this.visiblePages + 1;
}
//Also endingActivePage would be simply equal visiblePagesEndRange.
endingVisiblePage = this.visiblePagesEndRange;
return {
beginningVisiblePage,
endingVisiblePage,
};
}
getActivePageIndices() {
// the beginning page index will be current active page multiplied by no of records.
let beginningPageIndex = (this.activePage - 1) * this.recordsPerPage;
// the ending page index will be minimum of total records and (beginning + records allowed per page);
let endingPageIndex = Math.min(
beginningPageIndex + this.recordsPerPage,
this.totalRecords
);
return { beginningPageIndex, endingPageIndex };
}
}
// All the render and using Paginator class logic comes here
(function () {
function nextPage() {
paginator.gotoNextPage();
render();
}
function prevPage() {
paginator.gotoPrevPage();
render();
}
function lastPage() {
paginator.gotoLastPage();
render();
}
function firstPage() {
paginator.gotoFirstPage();
render();
}
// Delegating event to the parent ul.
function gotoPage(event) {
if (event.target.nodeName === "BUTTON") {
const page = parseInt(event.target.dataset.item);
paginator.gotoPage(page);
render();
}
}
const paginationPages = document.querySelector(".pagination__pages");
paginationPages.addEventListener("click", gotoPage);
// paginator object
// list which is of length 346
// recordsPerPage = 6
// visiblePages = 6
const paginator = new Paginator(movieInfo.length, 20, 6);
// Method to render the pagination buttons;
function renderPages() {
const paginationPages = document.querySelector(".pagination__pages");
let html = "";
let { beginningVisiblePage, endingVisiblePage } =
paginator.getVisiblePagesRange();
for (let page = beginningVisiblePage; page <= endingVisiblePage; page++) {
const pageClass =
paginator.activePage === page
? "pagination__page-btn--active"
: "pagination__page-btn";
html += `<li class='pagination__page'>
<button data-item=${page} class=${pageClass}>${page}</button>
</li>`;
}
paginationPages.innerHTML = html;
}
// Method to render the list items
function renderList() {
// const list = document.querySelector(".list");
const mainContent = document.getElementById("main-content");
const { beginningPageIndex, endingPageIndex } =
paginator.getActivePageIndices();
let html = "";
for (let index = beginningPageIndex; index < endingPageIndex; index++) {
// html += `<li class='list__item'>${records[index]}</li>`;
html = `<div class="container">
<div class="card">
<figure class="card__thumb">
<img src= "${IMG_URL + movieInfo[index].poster_path}"/>
<figcaption class="card__caption">
<h2 class="card__title">${movieInfo[index].name}</h2>
<p class="card__snippet">${movieInfo[index].overview}</p>
Read more
</figcaption>
</figure>
</div>
</div>`;
}
mainContent.innerHTML += html;
}
// Main render function
function render() {
renderPages();
renderList();
}
render();
this.firstPage = firstPage;
this.lastPage = lastPage;
this.nextPage = nextPage;
this.prevPage = prevPage;
this.gotoPage = gotoPage;
})();

Make your IIFE a named function declaration (init() below). And execute that function as a callback for your async Promise resolution.
NOTE: you should surround your call to your async function with a try/catch block.
async function popularMovies() {
//...
return movieInfo;
}
popularMovies().then(init);
// Replace your IIFE with this
function init(movieInfo) {
//...
const paginator = new Paginator(movieInfo.length, 20, 6);
//...
}

Related

Give 1 point to each unique winner respectively in javascript

First of all, I hope you can understand my English. I'm not really good at English. I'd like to ask for help on giving 1 point to every unique user(a user that joined, a live stream to be exact) for every correct answer.
The issue I have in the current coding is that instead of giving points to each user respectively, it counts all users as one. What I mean by that is, for example: When user A wins, he gets(displays) 1 point. When user B wins, he gets(displays) 2 points. The points keep adding for every other user.
I'd like it to be like this: When user A wins, he gets(displays) 1 point. When user B wins he also gets(displays) 1 point too. That means each user has their own winning record.
I hope you can understand what I'm trying to explain.
Please ask if you need further explanation on things you don't understand.
I'd like to display the win count under/currently under the 'function addPhoto'.
Following is the current code:
Thanks in advance!
// DATA
let connection = new TikTokIOConnection(undefined);
let gameWords = [];
let gamegameSelectedWord = null;
let gameTimer = null;
let gameStatus = false;
let wins = 0;
// Config
let confComment = false;
let confLike = false;
let confShare = false;
let confJoin = false;
// START
$(document).ready(() => {
// Resize
function resizeContainer() {
let height = window.innerHeight;
let width = Math.round((9 / 16) * height);
$("#gameSize").html(width + 'x' + height);
$(".container").outerWidth(width);
$(".background").outerWidth(width);
$(".printer").outerWidth(width);
$(".animation").outerWidth(width);
// Paper
if (window.innerWidth >= 1366) {
var paperHeight = $("#paperContainer").outerHeight() - 20;
} else {
var paperHeight = $("#paperContainer").outerHeight() + 7;
}
$("#paper").outerHeight(paperHeight);
}
resizeContainer();
$(window).resize(function() {
resizeContainer();
});
// Connect
$("#targetConnect").click(function(e) {
// Check
if (gameStatus) {
let targetLive = $("#targetUsername").val();
connect(targetLive);
} else {
alert("Start game first!");
}
});
// Test
$("#btnPrepare").click(function(e) {
// Check sound
playSound(1);
playSound(2);
playSound(3);
playSound(4);
speakTTS(MSG_TEST);
// Populate dummy
for (let i = 0; i < 30; i++) {
addContent("<div style='text-align:center;'>Welcome ๐Ÿฅณ๐Ÿฅณ๐Ÿฅณ</div>");
}
// Load game
loadGame();
// Setting
loadSetting();
// Set
gameStatus = true;
});
// Save config
$("#btnSave").click(function(e) {
loadSetting();
});
})
/*GAME PLAY
*/
function speakTTS(msg) {
speak(msg, {
amplitude: 100,
pitch: 70,
speed: 150,
wordgap: 5
});
}
/*function scramble( s ) {
return s.replace(
/\b([a-z])([a-z]+)([a-z])\b/gi,
function( t, a, b, c ) {
b = b.split( /\B/ );
for( var i = b.length, j, k; i; j = parseInt( Math.random() * i ),
k = b[--i], b[i] = b[j], b[j] = k ) {}
return a + b.join( '' ) + c;
}
);
}
document.forms.f.onsubmit = function() {
this.elements.t.value = scramble( this.elements.t.value );
return false;
};
document.forms.f.elements.t.value =
scramble( gettext( document.getElementsByTagName( 'p' )[0] ) );
*/
function censor(word) {
let censored = [];
let length = word.length;
let target = Math.ceil(length / 2);
let range_start = 2;
let range_end = target;
for (let i = 0; i < length; i++) {
let c = word.charAt(i);
if (i >= range_start && i <= range_end) {
if (c === " ") {
censored.push(" ");
} else {
censored.push("*");
}
} else {
censored.push(c);
}
}
return censored.join("");
}
function copyArray(a) {
let b = [];
for (i = 0; i < a.length; i++) {
b[i] = a[i];
}
return b;
}
function shuffle(a) {
let j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return copyArray(a);
}
function countDown() {
// Counter
let timeleft = 60 * 4; // 4 Mins
// Clear
if (gameTimer != null) {
clearInterval(gameTimer);
}
// Start
gameTimer = setInterval(function() {
// Reset
if (timeleft <= 0){
clearInterval(gameTimer);
loadGame();
}
// Set
$("#gameTimeout").html(timeleft.toLocaleString() + "s");
timeleft -= 1;
}, 1000);
}
function loadGame() {
// Check
if (gameWords.length < 1) {
gameWords = shuffle(WORDS);
}
// Load
gameSelectedWord = gameWords.pop();
// Set remain words
$("#gameWords").html(gameWords.length);
// Check
if (typeof gameSelectedWord === 'string') {
// Normalize
splittedWord = gameSelectedWord.split("|");
gameSelectedWord = splittedWord[1];
// Set
$("#textGuess").html("<div style='font-size:70%;padding-bottom:5px;'>" + splittedWord[0] + "</div>" + censor(gameSelectedWord));
//$("#textGuess").html("<div style='font-size:70%;padding-bottom:5px;'>" + splittedWord[0] + "</div>" + scramble(gameSelectedWord));
// Timeout
countDown()
} else {
loadGame();
}
}
function checkWinner(data, msg) {
// Check type
if (typeof gameSelectedWord === 'string' && typeof msg === 'string') {
// Check answer
if (gameSelectedWord.trim().toLowerCase() == msg.trim().toLowerCase()) {
// Print Photo
addPhoto(data, "winner");
// Sound
playSound(4);
// Play TTS
let tssMsg = MSG_WINNER.replace("|username|", data.uniqueId);
speakTTS(tssMsg);
// Reload game
loadGame();
}
}
}
function loadSetting() {
// Load
confComment = $("#confComment").prop('checked');
confLike = $("#confLike").prop('checked');
confShare = $("#confShare").prop('checked');
confJoin = $("#confJoin").prop('checked');
}
/*LIVE TIKTOK
*/
function connect(targetLive) {
if (targetLive !== '') {
$('#stateText').text('Connecting...');
$("#usernameTarget").html("#"+targetLive);
connection.connect(targetLive, {
enableExtendedGiftInfo: true
}).then(state => {
$('#stateText').text(`Connected ${state.roomId}`);
}).catch(errorMessage => {
$('#stateText').text(errorMessage);
})
} else {
alert('Enter username first!');
}
}
function sanitize(text) {
return text.replace(/</g, '<')
}
function isPendingStreak(data) {
return data.giftType === 1 && !data.repeatEnd;
}
function playSound(mode) {
document.getElementById("sfx"+mode).play();
}
function addContent(payload) {
// Container
let content = $('#paper');
content.append("<div class='item'>" + payload + "</div>");
// Scroll top bottom
content.animate({ scrollTop: content.get(0).scrollHeight}, 333);
}
function addMessage(data, msg) {
// DATA
let userName = data.uniqueId;
let message = sanitize(msg);
// Check for voice
let command = message.split(" ")[0];
if (command == ":=say" || command == ":=cakap") {
// TTS
let cleanText = message.replace("=:say", "").replace("=:cakap", "");
speakTTS(cleanText);
} else {
// Check setting
if (confComment) {
// Add
addContent("<span style='font-weight: bold;'>" + userName + "</span>: " + message);
// Sound
playSound(1);
}
}
}
function addPhoto(data, mode) {
// DATA
let userName = data.uniqueId;
let userAvatar = data.profilePictureUrl;
let word = ['Nice going','Thatโ€™s better than ever','Thatโ€™s first class work','Iโ€™m impressed','Nothing can stop you now','Well done','Good job','You did it','Thatโ€™s the way','You rock','I knew you could do it','Keep up the good work','Thatโ€™s clever','Way to go','Outstanding','Tremendous','Fantastic','You are amazing','No one can beat you','You are the chosen one'];
let words = word[Math.floor(Math.random()*word.length)];
// Add
if (mode == "winner")
{
wins++;
addContent(
`<div style="text-align:center;font-size: 1.25rem;">
<div style='padding-bottom:.25rem;color:#1881FF;'>๐Ÿ‘๐Ÿป๐Ÿ‘๐Ÿป `+words+`</div>
<div style='padding-bottom:.5rem;font-weight: bold;color:#20B601;'>`+userName+` โ—</div>
<div>
<img src="`+userAvatar+`" style="width:135px;height:135px;border-radius: 15px;"/>
Wins: `+wins+`
</div>
</div>`
);
} else {
addContent(
`<div style="text-align:center;font-size: 1.25rem;">
<div style='padding-bottom:.25rem;'>๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰Thanks๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰</div>
<div style='padding-bottom:.5rem;font-weight: bold;color:#EA0C0C;'>`+userName+`</div>
<div>
<img src="`+userAvatar+`" style="width:135px;height:135px;border-radius: 15px;"/>
</div>
</div>`
);
}
// Sound
playSound(3);
}
function addGift(data) {
// DATA
let userName = data.uniqueId;
let giftPictureUrl = data.giftPictureUrl;
let giftName = data.giftName;
let giftRepeat = data.repeatCount;
let giftTotal = (data.diamondCount * data.repeatCount);
let word = ['Appreciate it','Thanks','Thank you very much','It means a lot','Youโ€™re the best','Your gift helps me',];
let words = word[Math.floor(Math.random()*word.length)];
// Check
if (giftTotal >= 10) {
// Print Photo
addPhoto(data);
} else {
// Add
addContent(
`<div style="text-align:center;font-size: 1.25rem;"><div style='padding-bottom:.5rem;'>`+words+` <span style='font-weight: bold;color:#EA0C0C;'>`+userName+`!</span></div>
<div style='font-weight: bold;padding-bottom:.5rem;'><img src="`+giftPictureUrl+`" style="width:35px;height:35px;"/> Sent `+giftName+`</div>
x`+giftRepeat.toLocaleString()+` worth `+giftTotal.toLocaleString()+` coins!</div>`
);
// Sound
playSound(2);
// Play TTS
let tssMsg = MSG_GIFT.replace("|username|", userName);
speakTTS(tssMsg);
}
}
// New chat comment received
connection.on('chat', (data) => {
addMessage(data, data.comment);
checkWinner(data, data.comment);
})
// New gift received
connection.on('gift', (data) => {
if (!isPendingStreak(data) && data.diamondCount > 0) {
addGift(data);
}
})
// Like
connection.on('like', (data) => {
if (typeof data.totalLikeCount === 'number') {
// Check setting
if (confLike) {
// Print like
addMessage(data, data.label.replace('{0:user}', '').replace('likes', `${data.likeCount} likes`));
}
}
})
// Share, Follow
connection.on('social', (data) => {
// Check setting
if (confShare) {
// Print share
addMessage(data, data.label.replace('{0:user}', ''));
}
})
// Member join
let joinMsgDelay = 0;
connection.on('member', (data) => {
let addDelay = 250;
if (joinMsgDelay > 500) addDelay = 100;
if (joinMsgDelay > 1000) addDelay = 0;
joinMsgDelay += addDelay;
setTimeout(() => {
joinMsgDelay -= addDelay;
// Check setting
if (confJoin) {
// Print join
addMessage(data, "has entered");
}
}, joinMsgDelay);
})
// End
connection.on('streamEnd', () => {
$('#stateText').text('Stream ended.');
})
You should have an dictionary (object) of wins with keys the users, and values are the wins for each. Thus you save for each user its own wins.
So declare global:
let wins = {
// name: wins, name2: wins2
}
Then whenever need to increase wins, do so for the wins[playerName] value.
if (mode == "winner") {
wins[userName] = wins[userName] || 0
wins[userName]++
addContent(
`<div style="text-align:center;font-size: 1.25rem;">
<div style='padding-bottom:.25rem;color:#1881FF;'>๐Ÿ‘๐Ÿป๐Ÿ‘๐Ÿป `+ words + `</div>
<div style='padding-bottom:.5rem;font-weight: bold;color:#20B601;'>`+ userName + ` โ—</div>
<div>
<img src="`+ userAvatar + `" style="width:135px;height:135px;border-radius: 15px;"/>
Wins: `+ wins[userName] + `
</div>
</div>`
);
}

How to Remove Code Duplicates in Below Methods [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
$("#threeToThree").click(function() {
playSound("Start");
executeTimer();
var nextColor = arrangeThreeToThreeColors();
$("#grid .btn").click(function() {
animatePress(this);
if ($(this).attr("id") === nextColor) {
playSound("Success");
totalPoint += 10;
console.log("Total Point: " + totalPoint);
seenColors = [];
nextColor = arrangeThreeToThreeColors();
} else {
playSound("Failure");
}
});
});
$("#fourToFour").click(function() {
playSound("Start");
executeTimer();
var nextColor = arrangeFourToFourColors();
$("#grid .btn").click(function() {
animatePress(this);
if ($(this).attr("id") === nextColor) {
playSound("Success");
totalPoint += 10;
console.log("Total Point: " + totalPoint);
seenColors = [];
nextColor = arrangeFourToFourColors();
} else {
playSound("Failure");
}
});
});
$("#fiveToFive").click(function() {
playSound("Start");
executeTimer();
var nextColor = arrangeFiveToFiveColors();
$("#grid .btn").click(function() {
animatePress(this);
if ($(this).attr("id") === nextColor) {
playSound("Success");
totalPoint += 10;
console.log("Total Point: " + totalPoint);
seenColors = [];
nextColor = arrangeFiveToFiveColors();
} else {
playSound("Failure");
}
});
});
function arrangeThreeToThreeColors() {
for (let i = 0; i <= 12; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2) {
var randomColor = getValidColor();
manipulateButtons(i, randomColor);
seenColors.push(randomColor);
} else {
continue;
}
}
var nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
function arrangeFourToFourColors() {
for (let i = 0; i <= 18; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2 || mod === 3) {
var randomColor = getValidColor();
manipulateButtons(i, randomColor);
seenColors.push(randomColor);
} else {
continue;
}
}
var nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
function arrangeFiveToFiveColors() {
for (let i = 0; i <= 24; i++) {
var randomColor = getValidColor();
manipulateButtons(i, randomColor);
seenColors.push(randomColor);
}
var nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
How can I make this code clear code? What is the best way to make this code more readable? Is there a way to implement #threeToThree.click(), #fourToFour.click() and #fiveToFive.click() contents in only one method? And can I combine the contents of arrangeThreeToThreeColors(), arrangeFourToFourColors(), arrangeFiveToFiveColors() in one method?
Added Comments along the code update, take what part of the code refactor best suits you and you can ignore the reset or modify as you want.
1. Using Constants insted of string
const SoundStatus = {
Start: "Start",
Success: "Success",
Failure: "Failure"
};
2. Global variables and using single point of update.
Single point update, would seem too much for simple update. But this will helpful when debugging, as you would know where the variables gets updated.
/**
* I couldn't get the context of these 2 variables; assuming them to be global variables
*/
let seenColors = [];
let totalPoint = 0;
/**
* Using Single setter to update global variables; With this it will be easier to track there changes
*/
function UpdateTotalPoint() {
totalPoint += 10;
}
function ResetSeenColors() {
seenColors = [];
}
function UpdateSeenColors(color) {
seenColors.push(color);
}
3. Removing Duplicate code
/**
* Abstract attribute update to single function
*/
function updateNextAttribute() {
let nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
function ManipulateButtonAndUpdateSeenColorsWithRandomValue(value) {
const randomColor = getValidColor();
manipulateButtons(value, randomColor);
UpdateSeenColors(randomColor);
}
/**
* Remove contiune on else, as there is no statements to be executed of that; Its understood
*/
function arrangeFiveToFiveColors() {
for (let i = 0; i <= 24; i++) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
return updateNextAttribute();
}
function arrangeFourToFourColors() {
for (let i = 0; i <= 18; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2 || mod === 3) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
}
return updateNextAttribute();
}
function arrangeThreeToThreeColors() {
for (let i = 0; i <= 12; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
}
return updateNextAttribute();
}
/**
* Grid Updation
* arrangeColor; will be arrangeThreeToThreeColors, arrangeFourToFourColors or arrangeFiveToFiveColors
*
*/
function AnimateGridButton(nextColor, arrangeColor, context) {
$("#grid .btn").click(function () {
animatePress(context);
if ($(context).attr("id") === nextColor) {
playSound(SoundStatus.Success);
UpdateTotalPoint();
console.log("Total Point: " + totalPoint);
ResetSeenColors();
nextColor = arrangeColor();
} else {
playSound(SoundStatus.Failure);
}
});
}
/**
* Updating click listiner of 3-3, 4-4 & 5-5
*/
$("#threeToThree").click(function () {
initilize();
const nextColor = arrangeThreeToThreeColors();
AnimateGridButton(nextColor, arrangeThreeToThreeColors, this);
});
$("#fourToFour").click(function () {
initilize();
const nextColor = arrangeFourToFourColors();
AnimateGridButton(nextColor, arrangeFourToFourColors, this);
});
$("#fiveToFive").click(function () {
initilize();
const nextColor = arrangeFiveToFiveColors();
AnimateGridButton(nextColor, arrangeFiveToFiveColors, this);
});
/**
* Abstracting inital Start sound and executeTimer
*/
function initilize() {
playSound(SoundStatus.Start);
executeTimer();
}
Final code after Refactoring
const SoundStatus = {
Start: "Start",
Success: "Success",
Failure: "Failure"
};
let seenColors = [];
let totalPoint = 0;
function updateTotalPoint() {
totalPoint += 10;
}
function resetSeenColors() {
seenColors = [];
}
function updateSeenColors(color) {
seenColors.push(color);
}
function updateNextAttribute() {
let nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
function ManipulateButtonAndUpdateSeenColorsWithRandomValue(value) {
const randomColor = getValidColor();
manipulateButtons(value, randomColor);
updateSeenColors(randomColor);
}
function arrangeFiveToFiveColors() {
for (let i = 0; i <= 24; i++) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
return updateNextAttribute();
}
function arrangeFourToFourColors() {
for (let i = 0; i <= 18; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2 || mod === 3) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
}
return updateNextAttribute();
}
function arrangeThreeToThreeColors() {
for (let i = 0; i <= 12; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
}
return updateNextAttribute();
}
function AnimateGridButton(nextColor, arrangeColor, context) {
$("#grid .btn").click(function () {
animatePress(context);
if ($(context).attr("id") === nextColor) {
playSound(SoundStatus.Success);
updateTotalPoint();
console.log("Total Point: " + totalPoint);
resetSeenColors();
nextColor = arrangeColor();
} else {
playSound(SoundStatus.Failure);
}
});
}
$("#threeToThree").click(function () {
initilize();
const nextColor = arrangeThreeToThreeColors();
AnimateGridButton(nextColor, arrangeThreeToThreeColors, this);
});
$("#fourToFour").click(function () {
initilize();
const nextColor = arrangeFourToFourColors();
AnimateGridButton(nextColor, arrangeFourToFourColors, this);
});
$("#fiveToFive").click(function () {
initilize();
const nextColor = arrangeFiveToFiveColors();
AnimateGridButton(nextColor, arrangeFiveToFiveColors, this);
});
function initilize() {
playSound(SoundStatus.Start);
executeTimer();
}
After all the possible abstraction, we are left with code that is specific for each functions; except for ManipulateButtonAndUpdateSeenColorsWithRandomValue (Please come up with a proper name, that would fit the requirment). Couple of lines of abstraction is still possible for arrageColor related functions. Since I don't have a full picture of your requirement, I would leave that to you.

Click event listener is being repeatedly triggered without any clicks?

I am developing a "Battleship" game with two grids made up of divs and am currently attempting to add a click event listener to all of the divs.
The issue that I am having is that the event listener is being repeatedly triggered (until every single div has been clicked) when I refresh my page and I can't understand why...
Here's the event listener in question:
let aiGridCells = document.querySelectorAll(".ai-grid__game-cell");
aiGridCells.forEach(cell => {
cell.addEventListener("click", humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});
Where humanPlayer is an object that has been generated by a factory function:
const humanPlayer = playerFactory('human');
import gameboardFactory from './gameboardFactory';
const playerFactory = (name) => {
const playerBoard = gameboardFactory();
const humanAttack = (cell, player) => { // Where player is opponent
if (player.playerBoard.gameBoard[cell].id !== 'miss') {
player.playerBoard.receiveAttack(cell);
};
};
const aiAttack = (player) => { // Where player is opponent
const availableCells = [];
for (let i = 0; i < player.playerBoard.gameBoard.length; i++) {
if (player.playerBoard.gameBoard[i].id === null) {
availableCells.push(i);
};
};
const attackCell = Math.floor(Math.random() * availableCells.length);
player.playerBoard.receiveAttack(attackCell);
};
return {
name,
playerBoard,
humanAttack,
aiAttack
}
};
export default playerFactory;
and gameboardFactory is:
import shipFactory from './shipFactory';
const gameboardFactory = () => {
const gameBoard = [];
const shipYard = [];
const init = () => {
for (let i = 0; i<100; i++) {
gameBoard.push({id: null})
};
};
const checkValidCoordinates = (direction, start, end) => {
if (direction === 'horizontal') {
if ((start <= 9) && (end <= 9)) {
return true;
} else {
let newStart = (start/10).toString(10);
let newEnd = (end/10).toString(10);
if ((newStart.charAt(0)) === (newEnd.charAt(0))) {
return true;
};
};
} else {
if ((start <= 9) && (end <= 9)) {
return false
} else if (start <= 9) {
let newStart = start.toString(10);
let newEnd = end.toString(10);
if ((newStart.charAt(0)) === (newEnd.charAt(1))) {
return true;
};
} else {
let newStart = start.toString(10);
let newEnd = end.toString(10);
if ((newStart.charAt(1)) === (newEnd.charAt(1))) {
return true;
};
};
};
return false
};
const checkIfShipPresent = (direction, start, end) => {
if (direction === 'horizontal') {
for (let i = start; i <= end; i++) {
if (gameBoard[i].id !== null) {
return true;
}
};
return false;
} else {
for (let i = start; i <= end; i += 10) {
if (gameBoard[i].id !== null) {
return true;
}
};
return false;
};
};
const placeShip = (id, direction, start, end) => {
if (!checkValidCoordinates(direction, start, end)) {
return;
};
if (checkIfShipPresent(direction, start, end)) {
return;
};
const newShip = [];
if (direction === 'horizontal') {
for (let i = start; i <= end; i++) {
gameBoard[i].id = id;
newShip.push(i);
};
} else {
for (let i = start; i <= end; i += 10) {
gameBoard[i].id = id;
newShip.push(i);
};
};
shipYard.push(shipFactory(id, newShip));
};
const receiveAttack = (cell) => {
console.log(cell)
if (gameBoard[cell].id !== null) {
const attackedShip = shipYard.filter((ship) => {
return ship.id === gameBoard[cell].id;
})[0];
if (!attackedShip.hits.includes(cell)) {
attackedShip.hits.push(cell);
};
} else {
gameBoard[cell].id = 'miss';
};
};
const checkIfAllShipsSunk = () => {
let allShipsSunk = true;
shipYard.forEach((ship) => {
if (ship.isSunk() === false) {
allShipsSunk = false;
};
});
return allShipsSunk;
};
if (gameBoard.length === 0) {
init();
};
return {
gameBoard,
placeShip,
receiveAttack,
shipYard,
checkIfAllShipsSunk
};
};
export default gameboardFactory;
I'm completely lost to what the issue could be and have tried countless things to rectify it. Any suggestions would be hugely appreciated.
Thank you!
You trying to add actual function call as listener here:
let aiGridCells = document.querySelectorAll(".ai-grid__game-cell");
aiGridCells.forEach(cell => {
cell.addEventListener("click", humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});
So on your event listener initialization you actually call your function instead of passing it as a listener.
You can pass it like this instead:
aiGridCells.forEach(cell => {
cell.addEventListener("click", () => humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});

How to pause the entire code for 1 second in javascript

I am trying to code an evolutionary neural network for the game snake. I already coded the neural network part and now I'd like to output the game of the best individual of every generation. For that I'm using the drawing library p5.js (https://p5js.org/).
In my code I am running a loop in where a new generation based on the last generation is created. Each individual of the generation will have to play the game and that is how they are rated. Now I want the best individual to be outputted after I let every individual play once.
Between each outputted turn of the game of the best individual I want the code to wait 500 milliseconds. How do I achieve that?
Here is the code I've already tried but here it only outputed the board after the last turn of each generation:
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
}
}
Another version but the waiting didn't work here:
let i = 0;
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
i++;
}, 1);
The code you provided should do what you asked for, I could only clean up some parts for you. Explain a bit better what is the problem you are facing.
// The function should be defined only once.
function drawBoard(board) { }
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
// Don't wait on first iteration
await new Promise(resolve => setTimeout(resolve, 500 * (turn ? 0 : 1 )));
drawBoard(bestGameTurns[turn]);
}
}
}
Original idea (discarded)
You can create a short function like that:
function pause(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Then in any async function you can call it like that:
async function () {}
// something happening
await pause(500);
// continue
}
The other idea
Now, the code in your question is not complete so this is kind of blind coding but.
So, first of all setInterval will be running the whole function every 1 millisecond (actually 4 ms, as this is the minimum in JS). Which means it will run those loops. I decided to focus on the part that was marked by you.
Instead of loop and trying to pause it, I ask that maybe rewriting the loop into function called each half second until condition is met would do?
Also, I move drawBoard outside
setInterval(async () => {
// ^^^^^^^^ <-- this should probably go
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
function tick(turn = 0) {
let board = bestGameTurns[turn];
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
drawBoard(board);
// here is "setTimeouted" loop
if (turn < bestGameTurns.length) {
setTimeout(tick, 500, turn + 1);
}
}
tick();
}, 1);
Thanks to everyone, your suggestions brought me to an idea. I found out that the problem was lying somewhere else. Because javascript only runs on the one thread (I think thats how its called), after we run over one generation, we have to stop that function to let another draw function, which runs every frame, draw the board. After it is drawn, the main function can continue. This is how it looks:
let isDrawn = false;
let currentBoard;
async function setup() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn], turn);
}
}
}
function step(board, turn) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
setTimeout(() => {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}, 1);
const nrOfCols = 10;
const nrOfRows = 10;
const fieldWidth = 20;
const nodeNrs = [24, 8, 8, 4];
const populationSize = 200;
const mutationRate = 0.01;
let population;
let game;
let isDrawn = false;
let currentBoard;
async function setup() {
createCanvas(500, 500);
population = new PopulationManager(populationSize);
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn]);
}
}
}
function step(board) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
function draw() {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}
function drawBoard(board) {
board.forEach((col, colNr) => {
col.forEach((field, rowNr) => {
fill(field.isSnake ? "green" : field.isFruit ? "red" : "black");
stroke(255);
rect(colNr*fieldWidth, rowNr*fieldWidth, fieldWidth, fieldWidth);
});
});
}
function play(game) {
setInterval(() => {
if (!game.lost) {
game.nextTurn();
drawBoard(game.board);
} else {
clearInterval(1);
}
}, 500);
}
class PopulationManager {
constructor(populationSize) {
this.population = createPopulation();
function createPopulation() {
let population = [];
for (let i = 0; i < populationSize; i++) {
let chromosomes = createRandomChromosomes();
let i = new Individual(chromosomes);
population.push(i);
}
return population;
function createRandomChromosomes() {
let arr = [];
let nrOfChromosomes = calcNrOfChromosomes();
for (let i = 0; i < nrOfChromosomes; i++)
arr.push(Math.random()*2-1);
return arr;
function calcNrOfChromosomes() {
let nr = 0;
for (let i = 0; i < nodeNrs.length - 1; i++)
nr += (nodeNrs[i] + 1)*nodeNrs[i + 1];
return nr;
}
}
};
}
createNewGeneration() {
let that = this;
getFitnessOfPop();
this.calcAvgFitness();
this.findBestIndividual();
let parents = selection();
breed(parents);
function getFitnessOfPop() {
that.population.forEach(iv => {
iv.fitness = iv.playGame();
});
that.population.sort((a, b) => a.fitness - b.fitness);
}
function selection() {
let totalFitness = that.population.map(iv => iv.fitness/* + 1 */).reduce((a,b) => a + b);
let allParents = [];
for (let i = 0; i < that.population.length/2; i++) {
allParents.push(selectRandomParents());
}
return allParents;
function selectRandomParents() {
let p1, p2;
do {
p1 = selectRandomParent();
p2 = selectRandomParent();
} while (p1 == p2);
return [p1, p2];
function selectRandomParent() {
let rdm = Math.random()*totalFitness;
return that.population.find((iv, i) => {
let sum = that.population.filter((iv2, i2) => i2 <= i).map(iv => iv.fitness /* + 1 */).reduce((a,b) => a + b);
return rdm <= sum;
});
}
}
}
function breed(allParents) {
that.population = [];
allParents.forEach(ps => {
let childChromosomes = crossOver(ps[0].chromosome, ps[1].chromosome);
childChromosomes = [mutate(childChromosomes[0]), mutate(childChromosomes[1])];
let child1 = new Individual(childChromosomes[0]);
let child2 = new Individual(childChromosomes[1]);
that.population.push(child1);
that.population.push(child2);
});
function crossOver(parent1Chromosome, parent2Chromosome) {
let crossingPoint = Math.round(Math.random()*parent1Chromosome.length);
let divided1 = divideChromosome(parent1Chromosome);
let divided2 = divideChromosome(parent2Chromosome);
let child1Chromosome = divided1[0].concat(divided2[1]);
let child2Chromosome = divided2[0].concat(divided1[1]);
return [child1Chromosome, child2Chromosome];
function divideChromosome(chromosome) {
let part1 = chromosome.filter((g, i) => i <= crossingPoint);
let part2 = chromosome.filter((g, i) => i > crossingPoint);
return [part1, part2];
}
}
function mutate(chromosome) {
chromosome = chromosome.map(g => {
if (Math.random() < mutationRate)
return Math.random()*2-1;
return g;
});
return chromosome;
}
}
}
calcAvgFitness() {
this.avgFitness = this.population.map(iv => iv.fitness).reduce((a, b) => a + b) / this.population.length;
}
findBestIndividual() {
let bestFitness = -1, bestIndividual;
this.population.forEach(i => {
if (i.fitness > bestFitness) {
bestFitness = i.fitness;
bestIndividual = i;
}
});
this.bestIndividual = bestIndividual;
}
}
class Individual {
constructor(chromosome) {
this.chromosome = chromosome;
this.fitness = 0;
this.game = createGame();
function createGame() {
let weights = convertChromosomeToWeights();
let game = new Game(weights);
return game;
function convertChromosomeToWeights() {
let weights = [];
for (let i = 0; i < nodeNrs.length - 1; i++) {
let lArr = [];
for (let j = 0; j < nodeNrs[i] + 1; j++) {
let nArr = [];
lArr.push(nArr);
}
weights.push(lArr);
}
chromosome.forEach((gene, geneIdx) => {
let lIdx = -1, minIdx, maxIdx = 0;
for (let i = 0; i < nodeNrs.length - 1; i++) {
let nr = 0;
for (let j = 0; j <= i; j++)
nr += (nodeNrs[j] + 1)*nodeNrs[j + 1];
if (geneIdx < nr) {
lIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let nIdx = -1;
for (let i = 0; i < nodeNrs[lIdx] + 1; i++) {
let nr = minIdx + nodeNrs[lIdx + 1];;
if (geneIdx < nr) {
nIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let wIdx = -1;
for (let i = 0; i < nodeNrs[lIdx + 1]; i++) {
let nr = minIdx + 1;
if (geneIdx < nr) {
wIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
weights[lIdx][nIdx][wIdx] = gene;
});
return weights;
}
}
}
playGame() {
while (!this.game.lost) {
this.game.nextTurn();
}
return this.game.score;
}
}
class Game {
constructor(weights) {
let that = this;
this.chromosome = flattenArray(weights);
this.nn = new NeuralNetwork(weights);
this.turnNr = 0;
this.score = 0;
this.lost = false;
this.board = createBoard();
this.snake = new Snake();
setupSnake();
this.createFruit();
this.turns = [JSON.parse(JSON.stringify(this.board))];
function createBoard() {
let board = [];
for (let colNr = 0; colNr < nrOfCols; colNr++) {
board[colNr] = [];
for (let rowNr = 0; rowNr < nrOfRows; rowNr++) {
let field = new Field(colNr, rowNr);
board[colNr][rowNr] = field;
}
}
return board;
}
function setupSnake() {
for (let i = 0; i < 4; i++)
that.addToTail([floor(nrOfCols/2) - i, floor(nrOfRows/2)]);
that.length = that.snake.body.length;
}
function flattenArray(arr) {
let flattened = [];
flatten(arr);
return flattened;
function flatten(arr) {
arr.forEach(e => {
if (Array.isArray(e))
flatten(e);
else
flattened.push(e);
});
}
}
}
addToTail(pos) {
this.snake.body.push(pos);
this.board[pos[0]][pos[1]].setSnake(true);
}
nextTurn() {
let that = this;
let direction = findDirection();
this.moveSnake(direction);
this.turns.push(JSON.parse(JSON.stringify(this.board)));
this.turnNr++;
checkEat();
function findDirection() {
let inputValues = [];
for (let i = 0; i < 8; i++) {
let distances = that.snake.look(i, that.board);
inputValues.push(distances.distToFruit);
inputValues.push(distances.distToWall);
inputValues.push(distances.distToBody);
}
let output = that.nn.getOutput(inputValues);
let probability = -1;
let direction = -1;
output.forEach((v, vIdx) => {
if (v > probability) {
probability = v;
direction = vIdx;
}
});
return direction;
}
function checkEat() {
let head = that.snake.body[0];
let headField = that.board[head[0]][head[1]];
if (headField.isFruit) {
that.snake.eat();
that.score++;
headField.setFruit(false);
that.createFruit();
}
}
}
createFruit() {
let field;
do {
let colNr = floor(random()*nrOfCols);
let rowNr = floor(random()*nrOfRows);
field = this.board[colNr][rowNr];
} while(field.isSnake);
field.setFruit(true);
}
moveSnake(newDirection) {
let that = this;
let oldBody = JSON.parse(JSON.stringify(that.snake.body));
moveTail();
makeSnakeLonger();
moveHead();
function moveTail() {
for (let i = oldBody.length - 1; i > 0; i--)
that.snake.body[i] = oldBody[i - 1];
}
function moveHead() {
let newHeadPosition = findNewHeadPos();
if (
newHeadPosition[0] >= nrOfCols || newHeadPosition[0] < 0 ||
newHeadPosition[1] >= nrOfRows || newHeadPosition[1] < 0
) {
that.lose();
return;
}
let newHeadField = that.board[newHeadPosition[0]][newHeadPosition[1]];
if (newHeadField.isSnake) {
that.lose();
return;
}
addNewHeadPos(newHeadPosition);
}
function findNewHeadPos() {
if (newDirection == 0) { //up
return [oldBody[0][0], oldBody[0][1] - 1];
} else if (newDirection == 1) { //right
return [oldBody[0][0] + 1, oldBody[0][1]];
} else if (newDirection == 2) { //down
return [oldBody[0][0], oldBody[0][1] + 1];
} else if (newDirection == 3) { //left
return [oldBody[0][0] - 1, oldBody[0][1]];
}
}
function makeSnakeLonger() {
if (that.snake.length > that.snake.body.length) {
that.addToTail(oldBody[oldBody.length - 1]);
} else {
removeFromTail(oldBody[oldBody.length - 1]);
}
}
function removeFromTail(pos) {
that.board[pos[0]][pos[1]].setSnake(false);
}
function addNewHeadPos(pos) {
that.snake.body[0] = pos;
that.board[pos[0]][pos[1]].setSnake(true);
}
}
lose() {
this.lost = true;
}
}
class Field {
constructor(col, row) {
this.col = col;
this.row = row;
this.isFruit = false;
this.isSnake = false;
}
setFruit(bool) {
this.isFruit = bool;
}
setSnake(bool) {
this.isSnake = bool;
}
}
class Snake {
constructor() {
this.body = [];
this.length = 4;
}
eat() {
this.length++;
}
look(direction, board) {
let distances = {distToFruit: 0, distToWall: 0, distToBody: 0};
let xDiff = getXDiff(direction), yDiff = getYDiff(direction);
let pos = [this.body[0][0] + xDiff, this.body[0][1] + yDiff];
let dist = 1;
while (pos[0] > 0 && pos[0] < nrOfRows && pos[1] > 0 && pos[1] < nrOfCols) {
if (board[pos[0]][pos[1]].isFruit && distances.distToFruit == 0) distances.distToFruit = dist;
if (board[pos[0]][pos[1]].isSnake && distances.distToBody == 0) distances.distToBody = dist;
pos[0] += xDiff, pos[1] += yDiff;
dist++;
}
distances.distToWall = dist;
return distances;
function getXDiff(direction) {
if (direction == 5 || direction == 6 || direction == 7) return -1;
else if (direction == 1 || direction == 2 || direction == 3) return 1;
return 0;
}
function getYDiff(direction) {
if (direction == 7 || direction == 0 || direction == 1) return -1;
else if (direction == 3 || direction == 4 || direction == 5) return 1;
return 0;
}
}
}
class NeuralNetwork {
constructor(weights) {
this.layers = createLayers();
this.layers = addWeights(this.layers, weights);
function createLayers() {
let layers = [];
let nrOfNodesGlobal;
nodeNrs.forEach((nrOfNodes, lNr) => {
nrOfNodesGlobal = nrOfNodes;
layers[lNr] = [];
for (let i = 0; i < nrOfNodes; i++) {
let node = createNode(lNr);
layers[lNr][i] = node;
}
if (lNr != nodeNrs.length - 1)
layers[lNr].push(new Bias());
});
return layers;
function createNode(lNr) {
if (lNr == 0) return new InputLayerNode();
else if (lNr == nrOfNodesGlobal - 1) return new OutputLayerNode();
else return new HiddenLayerNode();
}
}
function addWeights(layers, weights) {
for (let lNr = 0; lNr < layers.length - 1; lNr++) {
let l = layers[lNr];
l.forEach((n1, nNr) => {
for (let n2Nr = 0; n2Nr < layers[lNr+1].length - 1; n2Nr++) { //not including bias of next layer
let n2 = layers[lNr+1][n2Nr];
let weight = weights[lNr][nNr][n2Nr];
let w = new Weight(n1, n2, weight);
n1.addWeight(w);
}
});
}
return layers;
}
}
getOutput(inputValues) {
let output = [];
this.layers[0].forEach((inputNeuron, nNr) => {
if (nNr != this.layers[0].length - 1)
inputNeuron.addToInput(inputValues[nNr]);
});
this.layers.forEach((l, lNr) => {
calcOutputs(l);
if (lNr != this.layers.length - 1) {
l.forEach(n => {
n.feedForward();
});
} else {
output = l.map(n => n.output);
}
});
return output;
function calcOutputs(layer) {
layer.forEach(n => n.output = n.activationFunction(n.summedInput, layer.map(N => N.summedInput)));
}
}
log() {
console.log(this.weights, this.nodes);
}
}
class Node {
constructor() {
this.weights = [];
this.summedInput = 0;
}
addWeight(w) {
this.weights.push(w);
}
addToInput(input) {
if (input == NaN)
console.log("A");
this.summedInput += input;
}
feedForward() {
this.weights.forEach((w, wNr) => {
let input = w.weight*this.output;
w.to.addToInput(input);
});
}
}
class Bias extends Node {
constructor() {
super();
this.output = 1;
}
activationFunction(x, allXs) {
return x;
}
}
class InputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return x;
}
}
class HiddenLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return leakyReLU(x);
}
}
class OutputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return softmax(x, allXs);
}
}
class Weight {
constructor(from, to, weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
setWeight(newWeight) {
this.weight = weight;
}
}
function leakyReLU(x) {
if (x >= 0) return x;
else return 0.01*x;
}
function softmax(x, allXs) {
return Math.exp(x) / allXs.map(X => Math.exp(X)).reduce((a, b) => a+b);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
It's not working that well but a few improvements should make it better...
If you have any suggestions for improvements of the code, please let me know!
I tried to fix it into steps as I said in comments,I hope I have no mistake :
let i = 0;
async function step(bestGameTurns, turn)
{
if (turn == bestGameTurns.length)
return;
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(() => step(bestGameTurns, turn+1), 500));
await p;
}
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
step(bestGameTurns, 0);
i++;
}, 1);

Returning Object Property in Javascript Undefined

I have this code:
function Tree() {
this.capacity = 1;
this.contents = 0;
this.children = [];
this.divided = false;
this.pour = function(amount) {
this.contents += amount;
if (this.contents <= 1) {
return;
}
if (!this.divided) {
this.divide();
}
amount = this.contents - 1;
this.contents = 1;
for (let child in this.children) {
this.children[child].pour(amount * .5);
}
}
this.divide = function() {
this.children = new Array(2).fill(0).map(x => new Tree());
this.divided = true;
return;
}
this.getContents = function(row, currentRow) {
if (currentRow === undefined) {
currentRow = 0;
}
if (row === currentRow) {
console.log('Contents:', this.contents)
return this.contents;
}
if (this.divided) {
console.log(row, currentRow)
currentRow++;
this.children[0].getContents(row, currentRow);
} else {
return;
}
}
}
Upon creating a tree, pouring into it, and getting its contents using this:
let tree = new Tree();
tree.pour(10);
tree.getContents(1);
It should return 1 because the second rows contents is 1. It logs 1 in the console but does not return the correct value. I am curious to what is going wrong.
Edit: I looked at switching it to a class and it did not solve the problem:
class Tree {
constructor() {
this.capacity = 1;
this.contents = 0;
this.children = [];
this.divided = false;
}
pour(amount) {
this.contents += amount;
if (this.contents <= 1) {
return;
}
if (!this.divided) {
this.divide();
}
amount = this.contents - 1;
this.contents = 1;
for (let child in this.children) {
this.children[child].pour(amount * .5);
}
}
divide() {
this.children = new Array(2).fill(0).map(x => new Tree());
this.divided = true;
return;
}
getContents(row, currentRow) {
if (currentRow === undefined) {
currentRow = 0;
}
if (row === currentRow) {
console.log('Contents:', this.contents)
return this.contents;
}
if (this.divided) {
console.log(row, currentRow)
currentRow++;
this.children[0].getContents(row, currentRow);
} else {
return;
}
}
}
The console.log you are seeing is the result of this call:
if (this.divided) {
console.log(row, currentRow)
currentRow++;
this.children[0].getContents(row, currentRow); //<-- here
// this is calling getContents() but ignores the return value
but in that case you don't actually return anything, so the inner console.log() fires but the return value is undefined.
I'm not really sure what the code is supposed to do, but returning a value when that condition is met results in a return value for the whole function:
if (this.divided) {
console.log(row, currentRow)
currentRow++;
return this.children[0].getContents(row, currentRow);
It logs 1 to the console because you call
tree.pour(10)
Current row is undefined because you do not pass it in the argument
if (currentRow === undefined) {
currentRow = 0;
}
if (row === currentRow) {
console.log('Contents:->', this.contents)
return this.contents;
}
So it mean currentRow is 0 and row(1) is not equals to currentRow (0) which is why it returns undefined.
} else {
return;
}

Categories

Resources