Inside of my progress function, it will hit the base of the recursion, but the value im expecting to have returned does not change.
let graph = [[1,1,1],[1,1,1,],[1,1,1]]
function findPath(graph){
function progress(row, col){
if(row == graph.length-1 && graph[row][col]== 1) {
console.log('makes it here but does not return true !?')
return true;
}
//check right
if(graph[row][col+1] == 1) {
graph[row][col] = 2
progress(row, col+1);
}
// check left
if(graph[row][col-1] == 1) {
graph[row][col] = 2
progress(row, col-1);
}
// check down
if(graph[row+1][col] == 1){
graph[row][col] = 2
progress(row+1, col)
}
}
for(let i = 0; i < graph[0].length; i++) {
if(graph[0][i] == 1) {
if(progress(0, i)) {
return true;
}
}
}
return false;
}
console.log(findPath(graph))
This should return true, it hits the condition (logs the text) but then keeps moving, and always returns false.
Ok, recursion works with an stack, each call is stacked and just continues your execution after all the other call stacked after are done.
Like:
call1 -> call2 -> call3 -> callN
after reach the last call (callN), all the calls will be unstacket from back to front.
You just return true on the last call, but this value get lost when the function calls is unstacked
In other words, to your example works, you need to always return the value from the progress function.
I tried to adapty your code to work better:
let graph = [[1,1,1],[1,1,1,],[1,1,1]]
function findPath(graph){
function progress(row, col){
if(row == graph.length-1 && graph[row][col]== 1) {
return true;
}
//check right
if(graph[row][col+1] == 1) {
graph[row][col] = 2
var right = progress(row, col+1);
}
// check left
if(graph[row][col-1] == 1) {
graph[row][col] = 2
var left = progress(row, col-1);
}
// check down
if(graph[row+1][col] == 1){
graph[row][col] = 2
var down = progress(row+1, col)
}
// propagate result
return (right || left || down)
}
for(let i = 0; i < graph[0].length; i++) {
if(graph[0][i] == 1) {
if(progress(0, i)) {
return true;
}
}
}
return false;
}
console.log(findPath(graph))
I only look to the recursion part, and not for the problem it self, in my example, if in any path (right, left or down) i grab that value and pass back untill it reach my first function call. That way, the true value will be propagate until the end
Hope i have helped
Related
I add event handlers to all my inputs in a sudoku game using addEventListener.
Every time that I write a number inside an input handler is running 3 times, why?
I want it to run only once for each element.
How can I do that?
// passing the board game
function addListener(game) {
counter = 0;
for (let i = 0; i < 81; i++) {
//add evenListener to all the input filds
document.querySelectorAll('input')[i].addEventListener('input', function() {
//save the innerHtml of the value that we write
let inputInnerHTML = Number(this.value);
//save the id of the input that we write in
let index = this.id;
checkIfCorrect(inputInnerHTML, index, game)
})
};
}
function checkIfCorrect(inputInnerHTML, index, game) {
for (let i = 0; i < game.length; i++) {
//if the original number is the same, the number will be black
if (inputInnerHTML === 0) {
// if the input is erased remove the classes
document.getElementById(index).classList.remove('wrong');
document.getElementById(index).classList.remove('correct');
}
if (game[i] === inputInnerHTML) {
document.getElementById(index).classList.add('correct');
break;
} else if (game[i] !== inputInnerHTML && inputInnerHTML !== 0) {
//if the number is not the same, the number will be red
//and if we did not erase the number add red and count
document.getElementById(index).classList.add('wrong');
counter++
if (counter === 1) {
document.getElementById('mistakes').innerHTML = 'Mistakes: 1/3';
}
if (counter === 2) {
document.getElementById('mistakes').innerHTML = 'Mistakes: 2/3';
}
if (counter === 3) {
//go to you lose
document.getElementById('mistakes').innerHTML = 'Mistakes: 3/3';
youLose();
}
}
}
}
You are calling addEventListener every time you select a difficultyButton. This means that each time you click a button, you'll be adding an eventListener.
Fix this by calling removeEventListener, before calling addEventListener.
I think the problem is the checkIfCorrect function, after it is detected correct or not it doesn't stop and keep searching creating confusion.
By adding the break to the else if we block the search if the error has already been detected and notified.
function checkIfCorrect(inputInnerHTML, index, game) {
for (let i = 0; i < game.length; i++) {
//if the original number is the same, the number will be black
if (inputInnerHTML === 0) {
// if the input is erased remove the classes
document.getElementById(index).classList.remove('wrong');
document.getElementById(index).classList.remove('correct');
}
if (game[i] === inputInnerHTML) {
document.getElementById(index).classList.add('correct');
break;
} else if (game[i] !== inputInnerHTML && inputInnerHTML !== 0) {
//if the number is not the same, the number will be red
//and if we did not erase the number add red and count
document.getElementById(index).classList.add('wrong');
counter++
if (counter === 1) {
document.getElementById('mistakes').innerHTML = 'Mistakes: 1/3';
}
if (counter === 2) {
document.getElementById('mistakes').innerHTML = 'Mistakes: 2/3';
}
if (counter === 3) {
//go to you lose
document.getElementById('mistakes').innerHTML = 'Mistakes: 3/3';
youLose();
}
break; // FIX
}
}
}
My code will only go through to the first if statement where it checks the value of key for headline1 etc... The first if statement works properly but it won't work with any of the following if statements when the first one isn't true. I've switched the second statement to the first where it checks for 'desc1' and then it works for that one only.
The purpose of this function is to check each key of an object and return the key when its value is over a certain length so I can add a class and show user some warning. This is in Vue JS so ads is in data and characterCheck is in computed property.
ads: [
{
headline1: '_keyword_',
headline2: 'Online',
headline3: 'Free',
desc1: 'Buy online _keyword_',
desc2: ' Vast collection of _keyword_',
finalurl: 'www.books.com',
path1: '',
path2: '',
boolean: true
}
]
characterCheck () {
for(var x = 0; x < this.ads.length; x++){
if(this.ads[x]['boolean'] == true) {
for(var key in this.ads[x]){
var length = this.ads[x][key].replace(/_keyword_/g, this.activeKeyword).length
if( key === 'headline1' || key === 'headline2' || key === 'headline3'){
if(length > 30){
return key
}
} else if( key == 'desc1' || key == 'desc2'){
if(length > 90){
return key
}
} else if( key == 'path1' || key == 'path2'){
if(length > 15){
return key
}
} else {
return false
}
}
}
}
}
When your first nested if condition fails, the code goes to next subsequent else-if. For some particular value, all the if and else-if block fails and code lands on final else block which contains a return statement.
If your code reaches even once there, the entire function execution immediately stops and false value is returned.
Since, you wish to wait as long as you have not looped through all the values, remove the else part and add a simple return statement to the end of the for loop like this:
function characterCheck () {
for(var x = 0; x < this.ads.length; x++) {
if(this.ads[x]['boolean'] == true) {
for(var key in this.ads[x]) {
var length = this.ads[x][key].replace(/_keyword_/g, this.activeKeyword).length
if( key === 'headline1' || key === 'headline2' || key === 'headline3') {
if(length > 30) {
return key
}
}
else if( key == 'desc1' || key == 'desc2') {
if(length > 90) {
return key
}
} else if( key == 'path1' || key == 'path2') {
if(length > 15) {
return key
}
}
}
}
}
return false
}
I am making a little game of Simon with jQuery. I have the functionality I want; start on page load, score, round numbers, etc, and the game works to an extent.
However, I still have a problem that I can't get my head around. I want to be able to prevent the user from being able to select the panels during the computer's turn. Currently, the user can trigger a sequence during the computer displaying its output, which causes havoc with buttons flashing and sounds going off.
The issue lies in setTimeout(). I tried to implement a variable 'cpuLoop' which turns to true when it's the computer's turn, and then back to false, but the implementation of setTimeout() means that there are still events on the event loop even after cpuLoop has been changed to false. The change to false changes immediately when of course it should wait until the setTimeout() has completed.
A similar problem is encountered when the reset button is clicked. When clicked, it should interrupt the setTimeout() events and restart the game. As it is, it continues outputting the computer's turn.
To get around this, I have attached the setTimeout() functions in the global scope and attempted to cut them off with clearInterval(var) but this seems to have no effect at the moment.
Here is my jQuery:
$(function(){
var counter = 0;
var cpuArray = [];
var cpuSlice = [];
var numArray = [];
var userArray = [];
var num = 1;
var wins = 0;
var losses = 0;
var cpuLoop = false;
// Initialise the game
function init(){
$('#roundNumber').html('1');
counter = 0;
cpuArray = [];
numArray = [];
userArray = [];
cpuLoop = false;
num = 1;
// Create cpuArray
function generateRandomNum(min, max){
return Math.floor(Math.random() * (max - min) + min);
}
for(var i = 1; i <= 20; i++){
numArray.push(generateRandomNum(0, 4));
}
for(var i = 0; i < numArray.length; i++){
switch(numArray[i]){
case 0:
cpuArray.push('a');
break;
case 1:
cpuArray.push('b');
break;
case 2:
cpuArray.push('c');
break;
case 3:
cpuArray.push('d');
break;
}
}
console.log('cpuArray: ' + cpuArray);
// Create a subset of the array for comparing the user's choices
cpuSlice = cpuArray.slice(0, num);
goUpToPoint(cpuSlice);
}
init();
var looperA, looperB, looperC, looperD;
// Cpu plays sounds and lights up depending on cpuArray
function cpuPlayList(input, time){
setTimeout(function(){
if(input === 'a'){
looperA = setTimeout(function(){
aSoundCpu.play();
$('#a').fadeOut(1).fadeIn(500);
}, time * 500);
} else if(input === 'b'){
looperB = setTimeout(function(){
bSoundCpu.play();
$('#b').fadeOut(1).fadeIn(500);
}, time * 500);
} else if(input === 'c'){
looperC = setTimeout(function(){
cSoundCpu.play();
$('#c').fadeOut(1).fadeIn(500);
}, time * 500);
} else if(input === 'd'){
looperD = setTimeout(function(){
dSoundCpu.play();
$('#d').fadeOut(1).fadeIn(500);
}, time * 500);
}
}, 1750);
};
// CPU takes its turn
function goUpToPoint(arr){
cpuLoop = true;
console.log('cpuLoop: ' + cpuLoop);
for(var i = 0; i < arr.length; i++){
cpuPlayList(arr[i], i);
}
cpuLoop = false;
console.log('cpuLoop: ' + cpuLoop);
}
// User presses restart button
$('.btn-warning').click(function(){
clearTimeout(looperA);
clearTimeout(looperB);
clearTimeout(looperC);
clearTimeout(looperD);
init();
});
// Array comparison helper
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
// User presses one of the four main buttons
function buttonPress(val){
console.log('strict?: ' + $('#strict').prop('checked'));
console.log('cpuSlice: ' + cpuSlice);
userArray.push(val);
console.log('userArray: ' + userArray);
if(val === 'a'){ aSoundCpu.play(); }
if(val === 'b'){ bSoundCpu.play(); }
if(val === 'c'){ cSoundCpu.play(); }
if(val === 'd'){ dSoundCpu.play(); }
// If the user selected an incorrect option
if(val !== cpuSlice[counter])
//Strict mode off
if(!$('#strict').prop('checked')){
// Strict mode off
alert('WRONG! I\'ll show you again...');
userArray = [];
console.log('cpuSlice: ' + cpuSlice);
goUpToPoint(cpuSlice);
counter = 0;
} else {
//Strict mode on
losses++;
$('#lossCount').html(losses);
ui_alert('You lose! New Game?');
return;
} else {
// User guessed correctly
counter++;
}
if(counter === cpuSlice.length){
$('#roundNumber').html(counter + 1);
}
if(counter === 5){
ui_alert('YOU WIN!');
$('#winCount').html(++wins);
return;
}
console.log('counter: ' + counter);
if(counter === cpuSlice.length){
console.log('num: ' + num);
cpuSlice = cpuArray.slice(0, ++num);
console.log('userArray:' + userArray);
userArray = [];
console.log('cpuSlice: ' + cpuSlice);
goUpToPoint(cpuSlice);
counter = 0;
}
}
// Button presses
$('#a').mousedown(function(){
if(!cpuLoop){
buttonPress('a');
}
});
$('#b').mousedown(function(){
if(!cpuLoop) {
buttonPress('b');
}
});
$('#c').mousedown(function(){
if(!cpuLoop){
buttonPress('c');
}
});
$('#d').mousedown(function(){
if(!cpuLoop){
buttonPress('d');
}
});
// jQuery-UI alert for when the user has either won or lost
function ui_alert(output_msg) {
$("<div></div>").html(output_msg).dialog({
height: 150,
width: 240,
resizable: false,
modal: true,
position: { my: "top", at: "center", of: window },
buttons: [
{
text: "Ok",
click: function () {
$(this).dialog("close");
init();
}
}
]
});
}
// Sound links
var aSoundCpu = new Howl({
urls: ['https://s3.amazonaws.com/freecodecamp/simonSound1.mp3'],
loop: false
});
var bSoundCpu = new Howl({
urls: ['https://s3.amazonaws.com/freecodecamp/simonSound2.mp3'],
loop: false
});
var cSoundCpu = new Howl({
urls: ['https://s3.amazonaws.com/freecodecamp/simonSound3.mp3'],
loop: false
});
var dSoundCpu = new Howl({
urls: ['https://s3.amazonaws.com/freecodecamp/simonSound4.mp3'],
loop: false
});
});
and here is a link to the app on codepen. Many thanks
This seemed to work OK for me for disabling user input during the computer's turn:
function goUpToPoint(arr){
cpuLoop = true;
console.log('cpuLoop: ' + cpuLoop);
for(var i = 0; i < arr.length; i++){
cpuPlayList(arr[i], i);
}
//cpuLoop = false;
setTimeout(function() {
cpuLoop = false;
}, arr.length * 500 + 1750);
console.log('cpuLoop: ' + cpuLoop);
}
Then for the reset button, put this with your globals above function init()
timeoutsArray = [];
and make these function edits:
// Cpu plays sounds and lights up depending on cpuArray
function cpuPlayList(input, time){
timeoutsArray.push(setTimeout(function(){
if(input === 'a'){
timeoutsArray.push(setTimeout(function(){
aSoundCpu.play();
$('#a').fadeOut(1).fadeIn(500);
}, time * 500));
} else if(input === 'b'){
timeoutsArray.push(setTimeout(function(){
bSoundCpu.play();
$('#b').fadeOut(1).fadeIn(500);
}, time * 500));
} else if(input === 'c'){
timeoutsArray.push(setTimeout(function(){
cSoundCpu.play();
$('#c').fadeOut(1).fadeIn(500);
}, time * 500));
} else if(input === 'd'){
timeoutsArray.push(setTimeout(function(){
dSoundCpu.play();
$('#d').fadeOut(1).fadeIn(500);
}, time * 500));
}
}, 1750));
};
// User presses restart button
$('.btn-warning').click(function(){
for(var i = 0; i < timeoutsArray.length; i++) {
clearTimeout(timeoutsArray[i]);
}
timeoutsArray = [];
init();
});
I think you were replacing some of your looperX variable values. Using an array to store all of your setTimeout functions guarantees that they all get cleared.
Your problem is that setTimeout is an asynchronous function, which means that once you called it, the code after it continue as if it is done.
If you want the code to wait until the end of your loop, you need to invoke it at the end of the setTimeout function.
You could split your function in two (in your case it's the goUpToPoint function), something like this:
function first_part() {
//Call setTimeout
setTimeout(function() { some_function(); }, time);
}
function second_part() {
// Rest of code...
}
function some_function() {
//Delayed code...
...
second_part();
}
Since you are calling your function a number of times, I would create a global counter that you can decrease at the end of each setTimeout call, and call the second_part function only if the counter is 0:
var global_counter = 0;
function first(num) {
//Call setTimeout
global_counter = num;
for (var i = 0; i < num; i++) {
setTimeout(function() { some_function(); }, time);
}
}
function second() {
// Rest of code...
}
function some_function() {
//Delayed code...
...
// Decrease counter
global_counter--;
if (global_counter == 0) {
second();
}
}
I'm trying to do some extremely simple form validation, my current problem is that my window.onload function doesn't call in the function I specify.
When I watch the flow of logic with firebug it just skips to the end of the code.
Here is an example of my code:
window.onload = init;
function init() {
var regForm = document.getElementById("registerform");
regForm.onsubmit = validatepostcode();
}
function validatepostcode() {
var postCode = document.getElementById("postcode");
var postCodeStr = postCode.charAt(0);
var state = document.getElementById("state");
var result = true;
if (postCodeStr == 3 || 8 && state == "Vic") {
result = true;
} else if (postCodeStr == (1 || 2) && state == "NSW") {
result = true;
} else if (postCodeStr == (4 || 9) && state == "QLD") {
result = true;
} else if (postCodeStr == 0 && state == "NT" || state == "ACT") {
result = true;
} else if (postCodeStr == 6 && state == "WA") {
result = true;
} else if (postCodeStr == 5 && state == "SA") {
result = true;
} else if (postCodeStr == 7 && state == "TAS") {
result = true;
} else
result = false;
if (result = false) {
alert("Your postcode does not match your state")
}
}
Five problems:
In init, you have this:
regForm.onsubmit = validatepostcode();
That calls validatepostcode and puts its return value in onsubmit. You probably meant to put the function itself it, not its return value in. Remove the parentheses:
regForm.onsubmit = validatepostcode;
In validatepostcode, you're fetching elements like this:
var postCode = document.getElementById("postcode");
…but then try to use them as values, like this:
var postCodeStr = postCode.charAt(0);
But an element and the current value of that element are not the same thing. More likely, you meant to retrieve the value on the first line:
var postCode = document.getElementById("postcode").value;
Same goes for state.
In validatepostcode, you have lines like this:
} else if (postCodeStr == (1 || 2) && state == "NSW") {
Specifically, 1 || 2 won't work like that. It will look at them like booleans and say, “one or two? well, they're both truthy…true it is!” and you'll essentially be doing
} else if (postCodeStr == true && state == "NSW") {
(Actually, it uses 1, not true, since the first operand was truthy, but that's not the important point here.)
Instead of using that abbreviated notation, you'll have to write it out longhand:
} else if ((postCodeStr == 1 || postCodeStr == 2) && state == "NSW") {
You mixed up = and == here:
if(result=false){
= will set result to false and leave the condition always false. Change it to == to test equality:
if(result==false){
You probably meant to return result at the end to prevent the form from being submitted when there is a validation error. With the other changes applied, you'd get an alert if there was a validation error, but it'd go on submitting anyway. As such, add a return result at the end of the validatepostcode function.
Im trying write JavaScript code which will generate a random number through a function then use that random number (between 1 and 6) to assign a value to the variable "background".
This is what I have:
function genBackground() {
var x=Math.floor((Math.random()*6)+1);
assignBG(x);
alert("test alert");
}
function assignBG(x) {
if (x === 1){
var background=blue;}
else if (x === 2){
var background=green;}
else if (x === 3){
var background=red;}
else if (x === 4){
var background=purple;}
else if (x === 5){
var background=yellow;}
else if (x === 6){
var background=orange;}
}
The alert "test alert" doesn't show, but does if but before the line "assignBG(x);". Have I done this bit wrong?
Are those "colors" defined previously in the code? Otherwise, you should write color = "white" and so on...
I'd also rewrite assignBG like this:
function genBackground() {
var x=Math.floor((Math.random()*6)+1);
var color = generateBG(x);
alert(color); // and do whatever you like with this color
}
function generateBG(x) {
var colors = ["blue","green","red","purple","yellow","orange"];
return colors[x];
}