Variable tracking user attempts does not update - javascript

I am working on a quiz game, and I have been having this issue for a while and I just can't figure out what I am doing wrong. Ask any question if you are confused by my explanation, i will be monitoring this post
How to recreate the problem - Type in the name displayed on the screen until you see "Game over bro!" -
Problem:
when I type in the name in the input field and click "Answer" to check if the input field value matches the name retrieved from the API, there is a variable(var attempts = 5) tracking how many times the user has attempted the question,but this variable(attempts) reduces it's value by one when the answer is correct, it should only do that when the answer is incorrect.
Also, let me know what you think about the JS code, is it bad code?
I am asking because the code in newReq function i wrote it twice, one loads and displays the data retrieved from the API when the page loads, the code inside the newReq function loads a new character when "New character" button is clicked.I was thinking about DRY the whole time, but i'm not sure how to load a new character without re-writing the code
var attemptsPara = document.querySelector("#attempts"),
attempts = 5,
scorePara = document.querySelector("#score"),
score = 0,
feedBackDiv = document.querySelector("#feedBack"),
newCharacterBtn = document.querySelector("#newCharacter"),
answerBtn = document.querySelector("#answer"),
input = document.querySelector("input");
scorePara.textContent = `Score is currently: ${score}`;
attemptsPara.textContent = `${attempts} attempts remaining`;
var feedBackText = document.createElement("p");
var characterPara = document.querySelector("#character");
//click new character button to load new character
// newCharacterBtn.addEventListener("click", () => {
// answerBtn.disabled = false;
// attempts = 5;
// attemptsPara.textContent = `${attempts} attempts remaining`;
// });
//function that displays retrieved data to the DOM
function displayCharacters(info) {
let englishName = info.attributes.name;
characterPara.textContent = `This is the character's name: ${englishName}`;
console.log(englishName, randomNumber);
}
//load new character
var randomNumber = Math.round(Math.random() * 100 + 2);
var request = new XMLHttpRequest();
request.open(
"GET",
"https://kitsu.io/api/edge/characters/" + randomNumber,
true
);
request.send();
request.onload = function() {
var data = JSON.parse(this.response);
var info = data.data;
displayCharacters(info);
//checks if the input value matches name retrieved
answerBtn.addEventListener("click", () => {
let englishName = info.attributes.name;
if (input.value === englishName) {
feedBackText.textContent = `${input.value} is correct`;
feedBackDiv.append(feedBackText);
feedBackDiv.style.backgroundColor = "green";
feedBackDiv.style.display = "block";
setTimeout(() => {
feedBackDiv.style.display = "none";
}, 3000);
score = score + 5;
scorePara.textContent = `Score is currently: ${score}`;
attempts = 5;
attemptsPara.textContent = `${attempts} attempts remaining`;
input.value = "";
newReq(); //call function to load and display new character
} else {
feedBackText.textContent = `${input.value} is wrong`;
feedBackDiv.append(feedBackText);
feedBackDiv.style.backgroundColor = "red";
feedBackDiv.style.display = "block";
input.focus();
setTimeout(() => {
feedBackDiv.style.display = "none";
}, 2000);
attempts = attempts - 1;
attemptsPara.textContent = `${attempts} attempts remaining`;
if (attempts <= 0) {
answerBtn.disabled = true;
attemptsPara.textContent = `Game over bro!`;
}
}
console.log(attempts); //check how many attempts remaining every time answerBtn is clicked
});
};
newCharacterBtn.addEventListener("click", newReq);
//function to make a new request and display it the information on the DOM,when New character button is clicked
function newReq() {
rand = randomNumber = Math.round(Math.random() * 100 + 2);
var request = new XMLHttpRequest();
request.open(
"GET",
"https://kitsu.io/api/edge/characters/" + randomNumber,
true
);
request.send();
request.onload = function() {
var data = JSON.parse(this.response);
var info = data.data;
displayCharacters(info);
answerBtn.addEventListener("click", () => {
let englishName = info.attributes.name;
if (input.value === englishName) {
feedBackText.textContent = `${input.value} is correct`;
feedBackDiv.append(feedBackText);
feedBackDiv.style.backgroundColor = "green";
feedBackDiv.style.display = "block";
//settimeout to hide feedBack div
setTimeout(() => {
feedBackDiv.style.display = "none";
}, 3000);
score = score + 5;
scorePara.textContent = `Score is currently: ${score}`;
attempts = 5;
attemptsPara.textContent = `${attempts} attempts remaining`;
input.value = "";
newReq();
} else if (input.value != englishName) {
feedBackText.textContent = `${input.value} is wrong`;
feedBackDiv.append(feedBackText);
feedBackDiv.style.backgroundColor = "red";
feedBackDiv.style.display = "block";
input.focus();
//settimeout to hide feedBack div
setTimeout(() => {
feedBackDiv.style.display = "none";
}, 2000);
attempts = attempts - 1;
attemptsPara.textContent = `${attempts} attempts remaining`;
if (attempts <= 0) {
answerBtn.disabled = true;
attemptsPara.textContent = `Game over bro!`;
}
}
});
console.log(attempts);
};
}
body {
margin: 0;
padding: 0;
background: black;
}
#imageHolder {
height: 560px;
width: 1100px;
background: #098;
margin: 10px auto;
}
#buttonHolder {
/* background: #453; */
width: 160px;
margin: 0 auto;
}
p,
h3 {
color: yellowgreen;
text-align: center;
}
h3 {
text-decoration: underline;
}
img {
width: 100%;
height: 100%;
}
button,
input {
margin: 10px 10px;
border: none;
background: #098;
display: block;
}
input {
background: white;
}
/* for the question and awnswer game */
#feedBack {
background: #098;
height: 120px;
width: 320px;
margin: 10px auto;
display: none;
}
<p id="score"></p>
<p id="character"></p>
<input type="text"> <button id="answer">Answer</button> <button id="newCharacter">New Character</button>
<p id="attempts"></p>
<div id="feedBack">
</div>

Your problem arises from calling answerBtn.addEventListener every time the answer returns - which adds more and more listeners.
Add a console log at the beginning of the click event and you'll see that after the 2nd answer the click event happens twice, then three times on the 3rd answer, etc'.
This means that on the first click the result is correct, but then on the rest of the clicks it is incorrect and must be causing the bug.
You should only listen to the event once, the variables that the event uses change and that should be sufficient. I can't fix the code for you at this time, apologies.

Related

How would you increase a variables value every second using a function?

I am trying to make a variable increase every second. What should I include inside the function autoClicker, so that the variable clicks increase by 1 every second? Also, if there are any more problems in the code, could you point them out to me? Sorry if this question seems basic, I am still quite new to JavaScript.
// The variable we are trying to increase
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clicks += upgrade1;
document.getElementById("clicks").innerHTML = clicks;
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
autoClicker()
} else {
alert = "Sorry, you don't have enough clicks to buy this";
}
}
// The function I will use to increase clicks
function autoClicker() {}
You could create an AutoClicker class that has a start, pause, ad update function. It will be in charge of managing the setInterval id.
Edit: I updated it to include upgrade buttons and the target can now be manually clicked.
const upgrades = [{
cost: 50,
rate: 2
}, {
cost: 100,
rate: 4
}];
const main = () => {
const target = document.querySelector('.auto-clicker');
const span = document.querySelector('.info > span');
const btn = document.querySelector('.btn-toggle');
const clicker = new AutoClicker(target, 1000, (clicks) => {
span.textContent = clicks;
}).start();
initializeUpgrades(clicker, upgrades);
btn.addEventListener('click', (e) => {
e.target.textContent = clicker.isRunning() ? 'Start' : 'Pause';
clicker.toggle();
});
};
const initializeUpgrades = (clicker, upgrades) => {
const upgradeContainer = document.querySelector('.upgrades');
upgrades.forEach(upgrade => {
const btn = document.createElement('button');
btn.textContent = upgrade.cost;
btn.value = upgrade.rate;
btn.addEventListener('click', (e) => {
let cost = parseInt(e.target.textContent, 10);
let value = parseInt(e.target.value, 10);
if (clicker.clicks >= cost) {
clicker.clicks -= cost;
clicker.step = value
} else {
console.log(`Cannot afford the ${value} click upgrade, it costs ${cost} clicks`);
}
});
upgradeContainer.appendChild(btn);
});
};
class AutoClicker {
constructor(target, rate, callback) {
if (typeof target === 'string') {
target = document.querySelector(target);
}
this.target = target;
this.rate = rate;
this.callback = callback;
this.init();
}
init() {
this.step = 1;
this.clicks = 0;
this._loopId = null;
this.target.addEventListener('click', (e) => {
this.update();
});
}
isRunning() {
return this._loopId != null;
}
toggle() {
this.isRunning() ? this.pause() : this.start();
}
update() {
this.clicks += this.step;
if (this.callback) {
this.callback(this.clicks);
}
}
start() {
this.update(); // Update immediately
this._loopId = setInterval(() => this.update(), this.rate);
return this;
}
pause() {
clearInterval(this._loopId);
this._loopId = null;
return this;
}
}
main();
.wrapper {
width: 10em;
text-align: center;
border: thin solid grey;
padding: 0.5em;
}
.auto-clicker {
width: 4em;
height: 4em;
background: #F00;
border: none;
border-radius: 2em;
}
.auto-clicker:focus {
outline: none;
}
.auto-clicker:hover {
background: #F44;
cursor: pointer;
}
.info {
margin: 1em 0;
}
.upgrades {
display: inline-block;
}
.upgrades button {
margin-right: 0.25em;
}
<div class="wrapper">
<button class="auto-clicker"></button>
<div class="info">Clicks: <span class="clicks"></span></div>
<button class="btn-toggle">Pause</button>
<div class="upgrades"></div>
</div>
// The variable we are trying to increase
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clicks += upgrade1;
document.getElementById("clicks").innerHTML = clicks;
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
autoClicker()
} else {
alert = "Sorry, you don't have enough clicks to buy this";
}
}
// The function I will use to increase clicks
setInterval(function(){ clicks++;console.log(clicks); }, 1000);
Use setInterval to run a function at a specified interval. This will run increaseClicks every 1000 milliseconds (every second):
function increaseClicks() {
clicks++;
}
var interval = setInterval(increaseClicks, 1000);
Use clearInterval to stop running it:
clearInterval(interval);
You can omit var interval = if you don't want to use clearInterval:
setInterval(increaseClicks, 1000);
There might be several things to improve this code
the use of textContent is preferable to innerHTML, it checks if there are no html tags in the text
then using inline functions like ()=>{} are more useful but in this program it does'nt make a difference, where you to use it in object oriented context you could use it several ways
you don't need document.getElementById, you could just use id.
And finaly (this is just à random tip which has nothing to do with much of anything) you may consider branchless programming because ifs are expensive.
Stackoverflow Branchless Programming Benefits
But anyways you should have fun :)
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clk.textContent = (clicks += upgrade1);
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
setInterval(()=>{getClicks();},1000);
} else {
alert("Sorry, you don't have enough clicks to buy this");
}
}
clk.onclick=()=>{getClicks();};
b.onclick=()=>{buyAutoClicker();};
html,body{height:100%;width:100%;margin:0;}
p{height:50px;width:50px;background:red;}
<p id="clk"></p>
<p id="b"></p>

HTML: How to get multiple timers to combine

I am building a timer based on user inputs. Below is how to code should work:
User inputs:
Time on: 30s
Time off: 10s
Number of sets: 4
Number of exercises per set: 5
Rest in between sets: 30s
With the above user inputs the timer will do this:
Hopefully that makes sense on what It is supposed to do. I am currently trying to implement the rest in between sets. Does anyone know how I could make that happen with how my code currently is? Also totalTime already includes the amount of time the rest in between sets would add on if that helps.
var numsets = document.getElementById("userInput1");
var numex = document.getElementById("userInput2");
var numwork = document.getElementById("userInput3");
var numrest = document.getElementById("userInput4");
var numrestafterset = document.getElementById("userInput5");
var sets;
var OGtimeon;
var OGtimeoff;
var totalTime;
var timeRemaining;
var hasBeenStarted = false; // Boolean value to test what time to use
var isTimeON = true;
var timeon;
var timeoff;
var timeonRemaining;
var timeoffRemaining;
var setsRemaining;
var OGsets;
var Prepare;
var OGExPS;
var OGTOASets;
var ExercisePS;
var RestAfterS;
var Intervals
var j = 0;
var ExercisesRemaining;
var RestRemaining;
// function sleep(milliseconds) { //function I found online to create a sleep function
// const date = Date.now();
// let currentDate = null;
// do {
// currentDate = Date.now();
// } while (currentDate - date < milliseconds);
// }
// function updateRest() {
// if (RestAfterS > 0) {
// interval3 = setInterval(RestCount, 1000);
// }
// else {
// startTime();
// }
// function RestCount() {
// while (RestAfterS != 0) {
// RestAfterS--;
// }
// j = 0;
// }
function updatePrep() {
if (hasBeenStarted == false) {
Prepare = 5;
interval2 = setInterval(PrepCount, 1000);
}
else {
startTime();
}
}
function PrepCount() {
let seconds = parseFloat(Prepare) % 60;
if (Prepare == 0) {
clearInterval(interval2);
startTime();
}
else {
PWR.innerHTML = "Get Ready!";
textarea.innerHTML = Prepare;
console.log(Prepare);
Prepare--;
}
}
function startTime() {
// Set values in code
OGsets = numsets.value;
OGtimeon = numwork.value;
OGtimeoff = numrest.value;
OGTOASets = numrestafterset.value;
OGExPS = numex.value;
timeon = (hasBeenStarted)? timeonRemaining : OGtimeon;
timeoff = (hasBeenStarted)? timeoffRemaining : OGtimeoff;
sets = (hasBeenStarted)? setsRemaining : OGsets;
ExercisePS = (hasBeenStarted)? ExercisesRemaining : OGExPS;
RestAfterS = (hasBeenStarted)? RestRemaining : OGTOASets;
// How much time on timer
// Var = (expression)? true : false this is basically an if statement
totalTime = (hasBeenStarted)? timeRemaining : ((parseFloat(OGtimeon)*parseFloat(sets)*parseFloat(ExercisePS))+(parseFloat(OGTOASets)*(parseFloat(sets)-1))+(parseFloat(OGtimeoff)*(parseFloat(sets)*(parseFloat(ExercisePS)-1))));
Intervals = ((parseFloat(sets)*parseFloat(ExercisePS))+((parseFloat(sets)-1))+((parseFloat(sets)*(parseFloat(ExercisePS)-1))));
hasBeenStarted = true;
// Start timer
interval = setInterval(updateCountdown, 1000);
}
function updateCountdown() {
IntervalsLeft.innerHTML = Intervals;
setsLeft.innerHTML = sets;
var minutes= Math.floor (parseFloat(totalTime) / 60);
var seconds = parseFloat(totalTime) % 60;
if (seconds < 10) {
textareaRemaining.innerHTML = minutes + ":0" + seconds;
} else {
textareaRemaining.innerHTML = minutes + ":" + seconds;
}
// Update TimeON / Time OFF
if(isTimeON){
PWR.innerHTML = "Work!";
textarea.innerHTML = timeon;
timeon--;
if(timeon == 0){
isTimeON = false;
timeon = OGtimeon;
Intervals--;
IntervalsLeft.innerHTML = Intervals;
}
}
//BELOW IS THE AREA I AM STUCK ON
else{
textarea.innerHTML = timeoff;
timeoff--;
PWR.innerHTML = "Rest!";
if(timeoff == 0){
isTimeON = true;
timeoff = OGtimeoff;
j++;
Intervals--;
IntervalsLeft.innerHTML = Intervals;
if (j == OGExPS) {
sets--;
//updateRest();
j = 0;
}
}
}
if( totalTime == 0 ){
clearTimeout(interval);
hasBeenStarted = false;
console.log(sets);
sets--;
setsLeft.innerHTML = sets;
PWR.innerHTML = "OMG YOU'RE DONE";
}
totalTime--;
}
function updateRest() {
if (RestAfterS > 0) {
interval3 = setInterval(RestCount, 5000);
}
else {
startTime();
}
}
function RestCount() {
while (RestAfterS != 0) {
RestAfterS--;
PWR.innerHTML = "Set Rest!";
textarea.innerHTML = RestAfterS;
}
j = 0;
clearInterval(interval3);
}
function stop(){
timeRemaining = totalTime;
timeonRemaining = timeon;
timeoffRemaining = timeoff;
RestRemaining = RestAfterS;
ExercisesRemaining = OGExPS;
setsRemaining = sets;
clearTimeout(interval);
// document.getElementById("Counter").innerHTML = j;
}
p {
display: inline-flex;
align-items: center;
}
label {
float: left;
display: block;
}
#userInput1 {
display: flex;
margin-bottom: 10px;
}
#userInput2 {
display: flex;
margin-bottom: 10px;
}
#userInput3 {
display: flex;
margin-bottom: 10px;
}
#userInput4 {
display: flex;
margin-bottom: 10px;
}
#userInput5 {
display: flex;
margin-bottom: 10px;
}
#Prepare {
display: flex;
margin-bottom: 10px;
}
#sets {
display: flex;
margin-bottom: 10px;
}
#timeon {
display: flex;
margin-bottom: 10px;
}
#timeoff {
display: flex;
margin-bottom: 10px;
}
#TotalTime {
display: flex;
margin-bottom: 10px;
}
#Counter {
display: flex;
margin-bottom: 10px;
}
input {
height: 20px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-compatible" content="ie=edge" />
<title>Document</title>
<link rel="stylesheet" href="Countdown From Scratch.css" />
</head>
<script src="Countdown From Scratch.js" defer></script>
<body>
<label>Number of Sets </label>
<input id="userInput1" type="numsets" value = "0"/>
<label>Number of Exercises per Set </label>
<input id="userInput2" type="numex" value = "0"/>
<label>Time to Work </label>
<input id="userInput3" type="numwork" value = "0"/>
<label>Time to Rest </label>
<input id="userInput4" type="numrest" value = "0"/>
<label>Time Inbetween Sets </label>
<input id="userInput5" type="numrestafterset" value = "0"/>
<p id="Prepare"></p>
<div id="sets"> </div>
<div id="timeon"> </div>
<div id="timeoff"> </div>
<div id="TotalTime"> </div>
<textarea id="textarea" placeholder="00:00"></textarea>
<textarea id="PWR" placeholder="Hello!"></textarea>
<textarea id="textareaRemaining" placeholder="00:00"></textarea>
<textarea id="setsLeft" placeholder="00"></textarea>
<textarea id="IntervalsLeft" placeholder="00"></textarea>
<button onclick="updatePrep()">Start time</button>
<button onclick="stop()">Stop time</button>
</body>
</html>
I think I have a rough idea of what you are trying to accomplish here, but please let me know if I have misinterpreted anything.
Suppose you want to execute a sequence of effects with some duration in between them. To keep the example small, we'll use console.log as a stand-in for any effect you'd want to execute (for instance, setting the innerHTML of a DOM element).
As a first example, here we wait for 1 second, display "Hello", then wait for 1.5 seconds and display "There":
setTimeout(() => {
console.log('Hello');
setTimeout(() => {
console.log('There');
}, 1500);
}, 1000);
We can clarify this by describing the common parts as a function:
function logAfter(message, delay, callback) {
setTimeout(() => {
console.log(message);
if (callback) callback();
}, delay);
}
Then our example can be written as:
logAfter('Hello', 1000, () => {
logAfter('There', 1500);
});
As another example that is perhaps more relevant to your project, here is how we can create a "loop" that performs an effect multiple times:
function logNAfter(message, times, delay, callback) {
if (times === 0 && callback) {
callback();
}
else {
setTimeout(() => {
console.log(message);
logNAfter(message, times-1, delay, callback);
}, delay);
}
}
We could use this to display "Hi There!" 3 times, each separated by half a second, and then display "Goodbye!" once, 2 seconds later:
logNAfter('Hi There!', 3, 500, () => {
logAfter('Goodbye!', 2000);
});
In theory, you could create an arbitrarily-long sequence of appropriately spaced effects in this way. However, I should also mention that many would prefer to describe these effects as Promises instead:
function logAfter(message, delay) {
return new Promise(resolve => {
setTimeout(() => {
console.log(message);
resolve();
}, delay);
});
}
function logNAfter(message, times, delay) {
if (times === 0) {
return Promise.resolve();
} else {
return logAfter(message, delay)
.then(() => logNAfter(message, times-1, delay));
}
}
The original examples then become:
logAfter('Hello', 1000)
.then(() => logAfter('There', 1500));
logNAfter('Hi There!', 3, 500)
.then(() => logAfter('Goodbye!', 2000));
This is arguably only a slight improvement over the callback-style approach above, but it is much clearer if you are able to use async/await:
async function logAfter(message, delay) {
return new Promise(resolve => {
setTimeout(() => {
console.log(message);
resolve();
}, delay);
});
}
async function logNAfter(message, times, delay) {
for (let i = 0; i < times; ++i) {
await logAfter(message, delay);
}
}
async function demoSequence1() {
await logAfter('Hello', 1000);
await logAfter('There', 1500);
}
async function demoSequence2() {
await logNAfter('Hi There!', 3, 500);
await logAfter('Goodbye!', 2000);
}
Hope that helps!
I like Williams suggested of breaking it down into different timers that could be controlled easier by the user. However, using this approach, I personally ran into the issue of how to pause the timer.
I decided to take a slightly different approach. From the table given, there is a fairly clear pattern of how the timer needs to be set up.The timer generally alternates between "Workout time" and "Rest time" for the whole duration. Rest time can take 3 different values:
Rest time between exercises
Rest time between Sets
The workout is complete
Since all of these values are known when the the values are inputed, my approach is set up the course of the timer before starting the timer. This creates a little bit of setup time but I believe this is negotiable and will not impact performance heavily. To do this, I created a created an array before starting the timer. Each item in this array contains data including the total time remaining, the current set, the label (Working out or resting), and the label timer. After creating this array, we can just create a timer that is based off the number of items in the array. I hope that this little explanation helps you understand my solution a little bit better.
Index.html
<!DOCTYPE html>
<html>
<style>
td, th {
text-align: left;
padding: 8px;
}
</style>
<head>
<title>Timer</title>
</head>
<body>
<!-- Inputs -->
<h2>Inputs</h2>
<p>
<label for="sets-input">Number of Sets: </label>
<input type="number" id="sets-input" min="1" max="3600">
</p>
<p>
<label for="exercises-input">Number of Exersizes per Set: </label>
<input type="number" id="exercises-input" min="1" max="3600">
</p>
<p>
<label for="workout-input">Exersise Time: </label>
<input type="number" id="workout-input" min="1" max="3600">
</p>
<p>
<label for="exersiseRest-input">Rest between exersises: </label>
<input type="number" id="exersiseRest-input" min="1" max="3600">
</p>
<p>
<label for="setRest-input">Rest between Sets: </label>
<input type="number" id="setRest-input" min="1" max="3600">
</p>
<!-- Buttons -->
<p>
<button id="start-button">Start</button>
<button id="reset-button">Reset</button>
</p>
<!-- Timer Display -->
<h2>Outputs:</h2>
<table>
<tr>
<th>Total Time Remaining: </th>
<td id="timer-display">???</td>
</tr>
<tr>
<th>Set Number: </th>
<td id="set-display">???</td>
</tr>
<tr>
<th id="label-display">???</th>
<td id="labelTimer-display">???</td>
</tr>
</table>
<script src="tabada.js"></script>
</body>
</html>
Tabada.js
//-----------------------------------------------------------------------------------------------
// GLOBAL VARIABLES
//-----------------------------------------------------------------------------------------------
// HTML
var setsInput = document.getElementById("sets-input");
var exersisesInput = document.getElementById("exercises-input");
var workoutInput = document.getElementById("workout-input");
var exersiseRestInput = document.getElementById("exersiseRest-input");
var setRestInput = document.getElementById("setRest-input");
var timerDisplay = document.getElementById("timer-display");
var setDisplay = document.getElementById("set-display");
var labelDisplay = document.getElementById("label-display");
var labelTimerDisplay = document.getElementById("labelTimer-display");
var startButton = document.getElementById("start-button");
var resetButton = document.getElementById("reset-button");
// JavaScript
var sets = 2;
var exersises = 3;
var workout = 5;
var exersiseRest = 2;
var setRest = 3;
var totalTime = -1;
var myInterval = -1;
var tabadaArray = [];
var tabadaIndex = 0;
//-----------------------------------------------------------------------------------------------
// BUTTON FUNCTIONS
//-----------------------------------------------------------------------------------------------
// Start / Pause Button
startButton.addEventListener("click", function(event){
// Set up Tabada Timer
if (totalTime == -1){
collectInputs(); // Comment this line for testing without inputs
calculateTotalTime();
createTabadaArray();
}
// Start timer
if (myInterval == -1){
startButton.innerHTML = "Pause";
myInterval = setInterval(tabadaTimer, 1000);
}
// Pause timer
else{
startButton.innerHTML = "Start";
clearInterval(myInterval);
myInterval = -1
}
});
// Reset Button
resetButton.addEventListener("click", function(event){
// Stop Timer
clearInterval(myInterval);
// Refresh Timer Display
calculateTotalTime();
updateOutputs(totalTime, 1, 'Workout', workout);
totalTime=-1; // Alows user to change input values before clicking start button.
// Reset start / pause button
myInterval = -1;
startButton.innerHTML = "Start";
});
//-----------------------------------------------------------------------------------------------
// SETUP FOR TABADA TIMER
//-----------------------------------------------------------------------------------------------
function collectInputs(){
sets = parseFloat(setsInput.value);
exersises = parseFloat(exersisesInput.value);
workout = parseFloat(workoutInput.value);
exersiseRest = parseFloat(exersiseRestInput.value);
setRest = parseFloat(setRestInput.value);
}
function calculateTotalTime(){
let totalWorkoutTime = workout * exersises * sets;
let totalExersiseRest = exersiseRest * (exersises - 1) * sets;
let totalSetsRest = setRest * (sets - 1);
totalTime = totalWorkoutTime + totalExersiseRest + totalSetsRest;
}
function createTabadaArray() {
tabadaIndex = 0; // Global variable used for tabada timer
tabadaArray = [];
for( let set=1; set<=sets; set++ ){
for( let exersise=1; exersise<=exersises; exersise++){
// Workout
addTimeBlock(set, 'Workout', workout);
// Exersise Rest
if ( exersise < exersises){
addTimeBlock(set, 'Rest', exersiseRest);
}
// Set Rest
else if( set < sets){
addTimeBlock(set, 'Rest', setRest);
}
// Done
else{break;} // Very end exersize has no rest, so we must break the loop.
}
}
}
function addTimeBlock(set, label, labelTime) {
// Add a sub timer to the array (workout, exersice rest, or set rest)
for (let i=labelTime; i>0; i--) {
tabadaArray.push({
"totalTimeRemaining" : totalTime--,
"set" : set,
"label" : label,
"labelTimeRemaining" : i,
});
}
}
//-----------------------------------------------------------------------------------------------
// TABADA TIMER
//-----------------------------------------------------------------------------------------------
function tabadaTimer(){
// Still time left
if (tabadaIndex < tabadaArray.length){
let displayInfo = tabadaArray[tabadaIndex];
updateOutputs( displayInfo.totalTimeRemaining,
displayInfo.set,
displayInfo.label,
displayInfo.labelTimeRemaining );
tabadaIndex++;
}
// End of tabada timer
else{
clearInterval(myInterval); // stop timer
updateOutputs(0, 1, 'Rest', 0);
totalTime = -1
}
}
function updateOutputs(totalTimeRemaining, setNumber, label, labelTimeRemaining){
timerDisplay.innerHTML = convertSeconds(totalTimeRemaining);
setDisplay.innerHTML = setNumber;
labelDisplay.innerHTML = label;
labelTimerDisplay.innerHTML = convertSeconds(labelTimeRemaining);
}
function convertSeconds(s){
// Seconds -> mm:ss format
// Calculate
let minutes = Math.floor(s/60);
let seconds = s%60;
// Format
let formattedminutes = ("0" + minutes).slice(-2);
let formattedseconds = ("0" + seconds).slice(-2);
return formattedminutes + ':' + formattedseconds;
}

Player interaction in a while javascript

i've encountered a few errors while working on a round by round script for a game on browser, sorry for my spelling mistakes.
I have a function with a while separated in three parts as the following (full javascript):
function turn(player, monster){
//player and monster are object
turns=2;
//few other unimportant variable
while(//as long as they have >0 hp){
if(turns==2){
//listen to click on html to know if player used a spell
// if he didn't in a few seconds, then he'll just use a normal
// attack
startingTurn = settimeout(startTurn, 2000);
sleep(2000);
turns=1;
}
if(turns==1 ){
//Turn of the player (working perfectly)
...
turns=0;
}
if(turns==0 and hp>0){
//Turn of the monster (working perfectly)
...
turns=2;
}
}
}
The problem that i've encountered is that in the listening of an action, i have to set a pause so that the player will have time to click on one of the spell.
To do this, i've tryed using "sleep" function to let 2 seconds for the player to act and a settimeout() to set a normal attack if there were no spell clicked.
The sleep function that i used looked like this one: https://www.sitepoint.com/delay-sleep-pause-wait/
Both those methods gave me errors:
-The settimeout() were effective only after the whole script(while) was finished (all at once).
-The "sleep" function caused the whole page to freeze, not only the while but also the html wasn't clickable.
So I'm looking for a way of leting the player interact while the function is running, if this isn't possible i will probably have to give up the interaction part.
I've looked online for quite a long time and didn't found any solution so i hope you can help me there!.
EDIT
I have tried to use the methods from Jacob (with async function and a loop() )because it was what looks the most clear for me.
But then i found the same first problem wich is that the settimeout() are executed after the loop() function here's where i think the problem is:
function attaqueBasic (attacker, defender) {
console.log("basic attack");
return new Promise((res, rej) => {
setTimeout(() => {
var dodge = dodge(defender);
if(dodge == false){
console.log("attack damage :"+attacker.dmg);
defender.life = defender.life - attacker.dmg;
console.log("life defender: "+defender.vie);
if(attacker.hasOwnProperty("bonus")==true && defender.life>0){
attackBonus(attacker, defender);
}
}
res()
}, 2000);
})
}
This is the function that let the player do a basic attack but it doesn't wait for the resolve to continue.
In the console i find the "basic attack" while the loop is running but never the following wich is in the settimeout and is executed only after all loops are done. Sorry if i made a big mistake here.
Here's the function loop() just in case i made some mistake:
function loop(perso, monstre){
nbtour += 1
if(nbtour>=6){
nbtour=0;
return;
}
console.log('---New tour'+nbtour+'---');
Turn(perso,monstre)
.then(checkWin(perso,monstre))
.then(Turn(monstre,perso))
.then(checkWin(perso,monstre))
.then(loop(perso, monstre))
.catch(() => {
console.log('End of combat, number of tours ='+nbtour);
nbtour=0;
})
}
Thanks for your time.
I assume this is on a browser or similar platform.
You can't have reasonably have meaningful input from the user within a while loop (I'm intentionally ignoring the unreasonable prompt function). Instead, handle an event that occurs when a player moves, and have that event handler process the changes the event causes. At the end of the handler's logic, check what used to be the loop termination condition ("as long as they have >0 hp") and if the termination condition has been reached, modify the page to say that the game is over or whatever (and probably disable the buttons).
For instance, here's a simple example using number guessing:
var number = Math.floor(Math.random() * 10) + 1;
var guesses = 3;
var btn = document.getElementById("guess");
var input = document.getElementById("guess-value");
btn.addEventListener("click", function() {
var value = input.value.trim();
if (!value) {
guessError("Please fill in a value");
return;
}
var guess = +value;
if (isNaN(guess)) {
guessError("Please only fill in simple numbers");
return;
}
if (guess < 1 || guess > 10) {
guessError("What part of 'between 1 and 10, inclusive' did you not understand? ;-)");
return;
}
if (guess === number) {
console.log("Congrats! You won!");
input.disabled = btn.disabled = true;
} else {
console.log("Nope, it's not " + guess);
if (--guesses === 0) {
console.log("You're out of guesses, the computer wins.");
input.disabled = btn.disabled = true;
} else {
input.value = "";
input.focus();
}
}
});
console.log("Guess a whole number between 1 and 10 inclusive");
input.focus();
function guessError(msg) {
console.log(msg);
input.focus();
}
<input type="text" id="guess-value">
<input type="button" id="guess" value="Guess">
That's very quick-and-dirty, but the key thing is that instead of a while loop with the number of times the user is allowed to guess, we just respond to them guessing and count the number of times they guess, and change the state of the page when they run out (or win).
First, we can not use while for game loop, because it will freeze browser, user would can not interact with game.
We will use recursion , something like this:
function loop () {
loop()
}
Second, we have to use async to stop loop to wait user operation, otherwise the recursion would have no difference with iteration, something like this:
setTimemout(attack, 2000)
Third, we have to chain all of the code in loop to make it run one by one, because they are async, otherwise the order cann't be guaranteed, like this:
firstAttack()
.then(secondAttack)
.then(thirdAttack)
.catch(gameOver())
Here is a simple demo.
const php = document.querySelector('.p-hp')
const mhp = document.querySelector('.m-hp')
const output = document.querySelector('.output')
const fireballEl = document.querySelector('.fireballEl')
let isAbility = false
let hp = ''
fireballEl.addEventListener('click' , e => {
isAbility = true
fireballEl.disabled = true
e.preventDefault()
})
function playerTurn () {
if (isAbility) {
isAbility = false
return fireball()
}
return slash()
}
function slash () {
return new Promise((res, rej) => {
setTimeout(() => {
const dmg = getRandomInt(1, 200)
hp.m -= dmg
if (hp.m < 0) {
hp.m = 0
}
mhp.textContent = hp.m
output.innerHTML += `<li class="player-info">You slashed monster which damaged ${dmg} hp</li>`
res()
}, 2000);
})
}
function fireball () {
return new Promise((res, rej) => {
setTimeout(() => {
const dmg = getRandomInt(260, 400)
hp.m -= dmg
if (hp.m < 0) {
hp.m = 0
}
mhp.textContent = hp.m
fireballEl.disabled = false
output.innerHTML += `<li class="player-info ability">You thrown a fireball to monster which damaged ${dmg} hp</li>`
res()
}, 2000);
})
}
function bite () {
return new Promise((res, rej) => {
setTimeout(() => {
const dmg = getRandomInt(6, 20)
hp.p -= dmg
if (hp.p < 0) {
hp.p = 0
}
php.textContent = hp.p
output.innerHTML += `<li class="monster-info">Monster bite you for ${dmg} hp</li>`
res()
}, 2000);
})
}
function check () {
if (hp.p <= 0) {
output.innerHTML += '<li class="lose">System terminated, rebuild enviroment, monster is ready,experiment can be continued... </li>'
return Promise.reject(1)
}
if (hp.m <= 0) {
output.innerHTML += '<li class="win">Hooray! Now the princess is yours.</li>'
return Promise.reject(0)
}
return Promise.resolve(1)
}
function init () {
output.innerHTML = ''
php.textContent = 100
mhp.textContent = 1000
return {
p: 100,
m: 1000
}
}
function updateDom () {
php.textContent = hp.p
mhp.textContent = hp.m
}
function loop () {
output.innerHTML += '<li class="bar">=====================</li>'
playerTurn()
.then(updateDom)
.then(check)
.then(bite)
.then(updateDom)
.then(check)
.then(loop)
.catch(() => {
startEl.disabled = false
fireballEl.disabled = true
})
}
function getRandomInt(min, max) {
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min)) + min
}
const startEl = document.querySelector('.start')
startEl.addEventListener('click', e => {
hp = init()
fireballEl.disabled = false
startEl.disabled = true
loop()
})
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.stage {
overflow:hidden;
margin: 0 auto;
max-width: 600px;
padding: 15px;
}
.player, .monster, .split {
width: 33%;
float: left;
}
.split {
font-size: 50px;
font-weight: 900;
}
h1 {
font-size: 16px;
margin-top: 0;
}
.output {
max-width: 600px;
margin: 0 auto;
border-top: 1px solid #333;
}
.output .player-info {
background-color: forestgreen;
color: #333;
}
.output .monster-info {
background-color:fuchsia;
color: #333;
}
.output .bar {
color: #eee;
/* margin: 3px 0; */
}
.output .ability {
background-color:yellow;
font-size: 16px;
}
.output .win {
font-size: 30px;
}
.output .lose {
font-size: 30px;
}
<div class="stage">
<div class="player">
<h1>Self-confident Player</h1>
<p>HP: <span class="p-hp">100</span></p>
<div class="abl">
Spell:
<button class="fireballEl" disabled>Inaccurate fireball</button>
</div>
</div>
<div class="split">
VS
<div>
<button class="start">Start to fight</button>
</div>
</div>
<div class="monster">
<h1>Young Monster</h1>
<p>HP: <span class="m-hp">1000</span></p>
</div>
</div>
<ul class="output">
</ul>

requestAnimationFrame gives the wrong timestamp when showing a confirm dialog (Chromium?)

I have a simple animation, which is done using requestAnimationFrame (for demo purposes adapted from the example on MDN). If before the animation I show a confirm dialog, the timestamp received by the animation function is wrong. The difference between the first and second timestamps is equal to the time from the moment the confirm message was shown, until the "OK" button was clicked. This behaviour (bug?) is visible in Chrome and Opera (both running Chromium). Firefox and Internet Explorer 11 run as expected. Check the fiddle or the example below.
const cache = {
start: null,
target: null
};
function animate(timestamp) {
console.log(timestamp);
if (cache.start === null) {
cache.start = timestamp;
}
var progress = timestamp - cache.start;
cache.target.style.left = Math.min(progress / 10, 100) + 'px';
if (progress < 1000) {
requestAnimationFrame(animate);
} else {
cache.target.style.left = 0;
cache.start = null;
}
}
(function() {
const target = document.getElementsByTagName("div")[0];
cache.target = target;
const cb = document.getElementsByTagName("input")[0];
const btn = document.getElementsByTagName("button")[0];
btn.addEventListener("click", function() {
if (cb.checked) {
if (confirm("Just click 'OK' to start the animation, ok?")) {
requestAnimationFrame(animate);
}
} else {
requestAnimationFrame(animate);
}
})
})();
html,
body {
padding: 0;
margin: 0;
}
div {
width: 50px;
height: 50px;
border: 1px solid black;
background-color: yellowgreen;
position: absolute;
top: 50px;
}
button {
margin-top: 20px;
}
<button type="button">Start</button>
<label>
<input type="checkbox" />use "confirm"</label>
<div>
</div>
Open the console to see the received timestamps. The animation is set to run for 2 seconds. When showing the confirm dialog, if the "OK" button gets clicked faster than 2 seconds, the animation runs for the "remaining" time. If the time needed to click the "OK" button is longer than the time animation time, the element will not be animated and there will be 2 values (timestamps) sent to the console; the difference of these 2 values is the time needed to click the "OK" button.
I assume that this is a bug in Chromium. Is there a workaround for this (still animating with requestAnimationFrame, not trough CSS)? I couldn't find anything regarding this in their tracker. Does anybody have additional info on this?
I have to say, I found this very interesting.
After spending to much time on it I may have found a workaround for you. You can see that here. https://jsfiddle.net/qtj467n0/13/
The basic gist of it is, I replaced the DOMHighResTimeStamp that requestAnimationFrame provides with performance.now() which also returns a DOMHighResTimeStamp.
const cache = {
start: null,
target: null,
time: 2000
};
function animate(timestamp) {
console.log(timestamp);
if (cache.start === null) {
cache.start = timestamp;
}
var progress = timestamp - cache.start;
cache.target.style.left = Math.min(progress / 10, cache.time / 10) + 'px';
if (progress < cache.time) {
requestAnimationFrame(animate);
} else {
cache.target.style.left = 0;
cache.start = null;
}
}
const render = () => {
requestAnimationFrame((timestamp) => {
const performanceNow = performance.now();
animate(performanceNow)
});
}
(function() {
const target = document.getElementsByTagName("div")[0];
cache.target = target;
const cb = document.getElementsByTagName("input")[0];
const btn = document.getElementsByTagName("button")[0];
btn.addEventListener("click", function() {
if (cb.checked) {
const confirmed = confirm("Just click 'OK' to start the animation, ok?");
if (confirmed) {
render();
}
} else {
requestAnimationFrame(animate);
}
})
})();
html,
body {
padding: 0;
margin: 0;
}
div {
width: 50px;
height: 50px;
border: 1px solid black;
background-color: yellowgreen;
position: absolute;
top: 50px;
}
button {
margin-top: 20px;
}
<button type="button">Start</button>
<label>
<input type="checkbox" />use "confirm"</label>
<div>
</div>

Checking function for sliding puzzle javascript

I created a sliding puzzle with different formats like: 3x3, 3x4, 4x3 and 4x4. When you run my code you can see on the right side a selection box where you can choose the 4 formats. The slidingpuzzle is almost done. But I need a function which checks after every move if the puzzle is solved and if that is the case it should give out a line like "Congrantulations you solved it!" or "You won!". Any idea how to make that work?
In the javascript code you can see the first function loadFunc() is to replace every piece with the blank one and the functions after that are to select a format and change the format into it. The function Shiftpuzzlepieces makes it so that you can move each piece into the blank space. Function shuffle randomizes every pieces position. If you have any more question or understanding issues just feel free to ask in the comments. Many thanks in advance.
Since I don't have enough reputation I will post a link to the images here: http://imgur.com/a/2nMlt . These images are just placeholders right now.
Here is the jsfiddle:
http://jsfiddle.net/Cuttingtheaces/vkyxgwo6/19/
As always, there is a "hacky", easy way to do this, and then there is more elegant but one that requires significant changes to your code.
Hacky way
To accomplish this as fast and dirty as possible, I would go with parsing id-s of pieces to check if they are in correct order, because they have this handy pattern "position" + it's expected index or "blank":
function isFinished() {
var puzzleEl = document.getElementById('slidingpuzzleContainer').children[0];
// convert a live list of child elements into regular array
var pieces = [].slice.call(puzzleEl.children);
return pieces
.map(function (piece) {
return piece.id.substr(8); // strip "position" prefix
})
.every(function (id, index, arr) {
if (arr.length - 1 == index) {
// last peace, check if it's blank
return id == "blank";
}
// check that every piece has an index that matches its expected position
return index == parseInt(id);
});
}
Now we need to check it somewhere, and naturally the best place would be after each move, so shiftPuzzlepieces() should be updated to call isFinished() function, and show the finishing message if it returns true:
function shiftPuzzlepieces(el) {
// ...
if (isFinished()) {
alert("You won!");
}
}
And voilà: live version.
How would I implement this game
For me, the proper way of implementing this would be to track current positions of pieces in some data structure and check it in similar way, but without traversing DOM or checking node's id-s. Also, it would allow to implement something like React.js application: onclick handler would mutate current game's state and then just render it into the DOM.
Here how I would implement the game:
/**
* Provides an initial state of the game
* with default size 4x4
*/
function initialState() {
return {
x: 4,
y: 4,
started: false,
finished: false
};
}
/**
* Inits a game
*/
function initGame() {
var gameContainer = document.querySelector("#slidingpuzzleContainer");
var gameState = initialState();
initFormatControl(gameContainer, gameState);
initGameControls(gameContainer, gameState);
// kick-off rendering
render(gameContainer, gameState);
}
/**
* Handles clicks on the container element
*/
function initGameControls(gameContainer, gameState) {
gameContainer.addEventListener("click", function hanldeClick(event) {
if (!gameState.started || gameState.finished) {
// game didn't started yet or already finished, ignore clicks
return;
}
if (event.target.className.indexOf("piece") == -1) {
// click somewhere not on the piece (like, margins between them)
return;
}
// try to move piece somewhere
movePiece(gameState, parseInt(event.target.dataset.index));
// check if we're done here
checkFinish(gameState);
// render the state of game
render(gameContainer, gameState);
event.stopPropagation();
return false;
});
}
/**
* Checks whether game is finished
*/
function checkFinish(gameState) {
gameState.finished = gameState.pieces.every(function(id, index, arr) {
if (arr.length - 1 == index) {
// last peace, check if it's blank
return id == "blank";
}
// check that every piece has an index that matches its expected position
return index == id;
});
}
/**
* Moves target piece around if there's blank somewhere near it
*/
function movePiece(gameState, targetIndex) {
if (isBlank(targetIndex)) {
// ignore clicks on the "blank" piece
return;
}
var blankPiece = findBlankAround();
if (blankPiece == null) {
// nowhere to go :(
return;
}
swap(targetIndex, blankPiece);
function findBlankAround() {
var up = targetIndex - gameState.x;
if (targetIndex >= gameState.x && isBlank(up)) {
return up;
}
var down = targetIndex + gameState.x;
if (targetIndex < ((gameState.y - 1) * gameState.x) && isBlank(down)) {
return down;
}
var left = targetIndex - 1;
if ((targetIndex % gameState.x) > 0 && isBlank(left)) {
return left;
}
var right = targetIndex + 1;
if ((targetIndex % gameState.x) < (gameState.x - 1) && isBlank(right)) {
return right;
}
}
function isBlank(index) {
return gameState.pieces[index] == "blank";
}
function swap(i1, i2) {
var t = gameState.pieces[i1];
gameState.pieces[i1] = gameState.pieces[i2];
gameState.pieces[i2] = t;
}
}
/**
* Handles form for selecting and starting the game
*/
function initFormatControl(gameContainer, state) {
var formatContainer = document.querySelector("#formatContainer");
var formatSelect = formatContainer.querySelector("select");
var formatApply = formatContainer.querySelector("button");
formatSelect.addEventListener("change", function(event) {
formatApply.disabled = false;
});
formatContainer.addEventListener("submit", function(event) {
var rawValue = event.target.format.value;
var value = rawValue.split("x");
// update state
state.x = parseInt(value[0], 10);
state.y = parseInt(value[1], 10);
state.started = true;
state.pieces = generatePuzzle(state.x * state.y);
// render game
render(gameContainer, state);
event.preventDefault();
return false;
});
}
/**
* Renders game's state into container element
*/
function render(container, state) {
var numberOfPieces = state.x * state.y;
updateClass(container, state.x, state.y);
clear(container);
var containerHTML = "";
if (!state.started) {
for (var i = 0; i < numberOfPieces; i++) {
containerHTML += renderPiece("", i) + "\n";
}
} else if (state.finished) {
containerHTML = "<div class='congratulation'><h2 >You won!</h2><p>Press 'Play!' to start again.</p></div>";
} else {
containerHTML = state.pieces.map(renderPiece).join("\n");
}
container.innerHTML = containerHTML;
function renderPiece(id, index) {
return "<div class='piece' data-index='" + index + "'>" + id + "</div>";
}
function updateClass(container, x, y) {
container.className = "slidingpuzzleContainer" + x + "x" + y;
}
function clear(container) {
container.innerHTML = "";
}
}
/**
* Generates a shuffled array of id-s ready to be rendered
*/
function generatePuzzle(n) {
var pieces = ["blank"];
for (var i = 0; i < n - 1; i++) {
pieces.push(i);
}
return shuffleArray(pieces);
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
}
body {
font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Helvetica, Arial, sans-serif;
font-size: 12px;
color: #000;
}
#formatContainer {
position: absolute;
top: 50px;
left: 500px;
}
#formatContainer label {
display: inline-block;
max-width: 100%;
margin-bottom: 5px;
}
#formatContainer select {
display: block;
width: 100%;
margin-top: 10px;
margin-bottom: 10px;
}
#formatContainer button {
display: inline-block;
width: 100%;
}
.piece {
width: 96px;
height: 96px;
margin: 1px;
float: left;
border: 1px solid black;
}
.slidingpuzzleContainer3x3,
.slidingpuzzleContainer3x4,
.slidingpuzzleContainer4x3,
.slidingpuzzleContainer4x4 {
position: absolute;
top: 50px;
left: 50px;
border: 10px solid black;
}
.slidingpuzzleContainer3x3 {
width: 300px;
height: 300px;
}
.slidingpuzzleContainer3x4 {
width: 300px;
height: 400px;
}
.slidingpuzzleContainer4x3 {
width: 400px;
height: 300px;
}
.slidingpuzzleContainer4x4 {
width: 400px;
height: 400px;
}
.congratulation {
margin: 10px;
}
}
<body onload="initGame();">
<div id="slidingpuzzleContainer"></div>
<form id="formatContainer">
<label for="format">select format:</label>
<select name="format" id="format" size="1">
<option value="" selected="true" disabled="true"></option>
<option value="3x3">Format 3 x 3</option>
<option value="3x4">Format 3 x 4</option>
<option value="4x3">Format 4 x 3</option>
<option value="4x4">Format 4 x 4</option>
</select>
<button type="submit" disabled="true">Play!</button>
</form>
</body>
Here we have the initGame() function that starts everything. When called it will create an initial state of the game (we have default size and state properties to care about there), add listeners on the controls and call render() function with the current state.
initGameControls() sets up a listener for clicks on the field that will 1) call movePiece() which will try to move clicked piece on the blank spot if the former is somewhere around, 2) check if after move game is finished with checkFinish(), 3) call render() with updated state.
Now render() is a pretty simple function: it just gets the state and updates the DOM on the page accordingly.
Utility function initFormatControl() handles clicks and updates on the form for field size selection, and when the 'Play!' button is pressed will generate initial order of the pieces on the field and call render() with new state.
The main benefit of this approach is that almost all functions are decoupled from one another: you can tweak logic for finding blank space around target piece, to allow, for example, to swap pieces with adjacent ids, and even then functions for rendering, initialization and click handling will stay the same.
$(document).on('click','.puzzlepiece', function(){
var count = 0;
var imgarray = [];
var test =[0,1,2,3,4,5,6,7,8,'blank']
$('#slidingpuzzleContainer img').each(function(i){
var imgalt = $(this).attr('alt');
imgarray[i] = imgalt;
count++;
});
var is_same = (imgarray.length == test.length) && imgarray.every(function(element, index) {
return element === array2[index];
});
console.log(is_same); ///it will true if two array is same
});
try this... this is for only 3*3.. you pass the parameter and makethe array value as dynamically..

Categories