Disable multiple clicks that accumulate interval points - javascript

I am building a timer that has a pause and resume function. I found out the user can set multiple intervals when the timer is clicked more than once.
How can I prevent users from setting multiple intervals?
I tried inserting ($('.clock)).off("click") once the interval has set, but then couldn't figure out how to resume it. I thought I could do a statement pause = true, but not sure how I can use it in my code.
let currentMins = 10
let currentCount = 10*60
let pause = true
$(document).ready(function(){
// --- SET TIME --- //
$('select').on('change', function(){
const timePair = {
pappardelle : 7,
penne : 10,
farfalle : 11,
bucatini : 8,
angelhair : 4,
gnocchi : 1,
orecchiette : 10,
justboiledeggs : 11
}
const selected = this.value
for(let keys in timePair){
let toPrint = ''
if(selected.split(' ').join('').toLowerCase() == keys){
toPrint = timePair[keys]
$('#mins').html(toPrint)
$('.clock').html(toPrint+':00')
currentMins = toPrint
currentCount = timePair[keys]*60
console.log('current counts on set: ',currentCount)
}
}
})
// --- UPDATE CLOCK --- //
//basic increment and decrement setting
$('.decrement').click(function(){
if((currentMins)>1){
currentMins-=1
currentCount-=60
$('#mins').html(currentMins)
$('.clock').html(currentMins + ':00')
console.log("current mins and count in decrement :", currentMins, currentCount)
}
})
$('.increment').click(function(){
if(currentMins<100){
currentMins+=1
currentCount += 60
$('#mins').html(currentMins)
$('.clock').html(currentMins + ':00')
console.log("current mins and count in increment :", currentMins, currentCount)
}
})
$('.clock').click(function(){
console.log("current currentCount in the starting clock div :", currentCount)
//interval setting
const interval = window.setInterval(function(){
if(currentCount == 0){
pause=true
$('.clock').html('Buon appetito!')
} else {
console.log("current currentCount in the else clause in clock div :", currentCount)
pause = false
currentCount --
let minuites = Math.floor(currentCount / 60)
let seconds = currentCount - minuites * 60
$('.clock').html(minuites + ':' + ('0' + seconds).slice(-2))
}
$('.pause').click(function(){
pause = true;
clearInterval(interval)
})
}, 1000)
$('select').on('change', function(){
pause = true;
clearInterval(interval)
})
})
})//end jquery

You can do that with a flag variable:
let started = false
and a conditional return statement:
if (started && !pause) {
return;
} else {
started = true;
}
All it does is when the clock is clicked, it checks to see if started is true. If it is, then the timer has already been enabled, so it just returns out of the function (unless it's paused). If the value of started is false, then the timer begins and the flag variable is set to true.
See this working example:
let currentMins = 10
let currentCount = 10 * 60
let pause = true
let started = false
$(document).ready(function() {
// --- SET TIME --- //
$('select').on('change', function() {
const timePair = {
pappardelle: 7,
penne: 10,
farfalle: 11,
bucatini: 8,
angelhair: 4,
gnocchi: 1,
orecchiette: 10,
justboiledeggs: 11
}
const selected = this.value
for (let keys in timePair) {
let toPrint = ''
if (selected.split(' ').join('').toLowerCase() == keys) {
toPrint = timePair[keys]
$('#mins').html(toPrint)
$('.clock').html(toPrint + ':00')
currentMins = toPrint
currentCount = timePair[keys] * 60
console.log('current counts on set: ', currentCount)
}
}
if (selected.indexOf('Seamless') != -1) {
window.open('http://seamless.com', '_blank')
}
})
// --- UPDATE CLOCK --- //
//basic increment and decrement setting
$('.decrement').click(function() {
if ((currentMins) > 1) {
currentMins -= 1
currentCount -= 60
$('#mins').html(currentMins)
$('.clock').html(currentMins + ':00')
console.log("current mins and count in decrement :", currentMins, currentCount)
}
})
$('.increment').click(function() {
if (currentMins < 100) {
currentMins += 1
currentCount += 60
$('#mins').html(currentMins)
$('.clock').html(currentMins + ':00')
console.log("current mins and count in increment :", currentMins, currentCount)
}
})
$('.clock').click(function() {
if (started && !pause) {
return;
} else {
started = true;
}
console.log("current currentCount in the starting clock div :", currentCount)
//interval setting
const interval = window.setInterval(function() {
if (currentCount == 0) {
pause = true
$('.clock').html('Buon appetito!')
} else {
console.log("current currentCount in the else clause in clock div :", currentCount)
pause = false
currentCount--
let minuites = Math.floor(currentCount / 60)
let seconds = currentCount - minuites * 60
$('.clock').html(minuites + ':' + ('0' + seconds).slice(-2))
}
$('.pause').click(function() {
pause = true;
clearInterval(interval)
})
}, 1000)
$('select').on('change', function() {
pause = true;
clearInterval(interval)
})
})
}) //end jquery
body {
margin: 50px;
font-family: 'Cormorant Garamond', serif;
color: tomato;
}
main {
justify-content: center;
}
h1 {
font-size: 40px;
text-align: center;
}
.grid {
display: grid;
grid-gap: 10px;
grid-template-columns: [col1-start] 130px [col2-start] 130px [col3-start] 140px [col3-end];
grid-template-rows: [row1-start] 120px [row2-start] 120px [row2-end];
background-color: #fff;
color: tomato;
justify-content: center;
}
.box {
color: tomato;
padding: 30px;
font-size: 150%;
border: 1px solid tomato;
}
.food {
grid-column: col1-start / col3-start;
grid-row: row1-start;
}
.clock {
grid-column: col3-start;
grid-row: row1-start / row2-end;
display: flex;
justify-content: center;
align-items: center;
}
.clock:hover {
color: #ffd700;
font-size: 25px;
cursor: pointer;
}
.settimer {
grid-column: col1-start;
grid-row: row2-start;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
align-content: stretch;
}
.settimer div {
margin: 5px;
}
#mins {
align-items: center;
font-size: 20px;
}
.icon {
font-size: 15px;
}
.icon:hover {
color: #ffd700;
cursor: pointer;
font-size: 18px;
}
.pause {
grid-column: col2-start;
grid-row: row2-start;
font-size: 20px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
align-content: stretch;
}
.pause:hover {
color: #ffd700;
cursor: pointer;
}
<!DOCTYPE html>
<html>
<head>
<script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="pomodoro.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Cormorant+Garamond:400,700" rel="stylesheet">
<script src="pomodorooo.js"></script>
<title>Pomodoro Clock</title>
</head>
<body>
<main>
<h1>Pomodoro clock</h1>
<div class="grid">
<div class="box food">Set the timer for
<select id="pasta">
<option id="0">I meant, pasta</option>
<option id="1">Pappardelle</option>
<option id="2">Penne</option>
<option id="3">Farfalle</option>
<option id="4">Bucatini</option>
<option id="5">Angel Hair</option>
<option id="6">Gnocchi</option>
<option id="7">Orecchiette</option>
<option id="8">Just boiled eggs</option>
<option id="9">Take me to Seamless already</option>
</select>
<!-- deleted form -->
</div>
<!-- a click box that has various food options, default set for each food -->
<div class="box clock">Start</div>
<!-- a blank circle. will be filled red-->
<div class="box settimer">
<div class="decrement icon"><i class="fas fa-caret-left"></i></div>
<div id="mins">Ready</div>
<!-- deleted span -->
<div class="increment icon"><i class="fas fa-caret-right"></i></div>
</div>
<!-- timer set. increment and decrement enabled -->
<div class="box pause">Pause</div>
<!-- break set. increment and decrement enabled -->
</div>
</main>
<br /><br /><br /><br /><br /><br />
</body>
</html>

Related

Why does my anime.js animation's not working everytime?

I got a problem with Anime.js, I got a project to do about a game with a dice and 2 players on the same screen. I would like to animate the background color and position depending on the active player (left or right). So I've made a div with a background, a negative z-index and a width of 50vw so it takes half of the screen. I placed the animation play() and reverse() into my switchPlayer function. My problem is that the background animation makes one "dead time" beetween 2 switch of player and I don't understand why.
Thanks for the help !
Here is my html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> -->
<title>Dice Game</title>
</head>
<body>
<div id="player-background"></div>
<!-- This is whre the whole game is contained -->
<div class="gameContainer">
<!-- Player one content -->
<div id="player1">
<h2>PLAYER 1</h2>
<span class="score" id="globalP1">0</span>
<div class="current">
<span class="cText">Current :</span>
<span id="roundP1">0</span>
</div>
</div>
<!-- Center content -->
<div id="center">
<button class="NewGame" id="new-game" type="button">NEW GAME</button>
<img alt="Dice" id="img" src="./images/1.png">
<button class="roll-dice" id="roll" type="button">ROLL DICE !</button>
<button id="hold" type="button">HOLD</button>
</div>
<!-- Player two content -->
<div id="player2">
<h2>PLAYER 2</h2>
<span class="score" id="globalP2">0</span>
<div class="current">
<span class="cText">Current :</span>
<span id="roundP2">0</span>
</div>
</div>
</div>
<script src="./node_modules/animejs/lib/anime.min.js"></script>
<script src="./script.js"></script>
</body>
</html>
My css
#import url('https://fonts.googleapis.com/css2?family=Lato:wght#300;400&display=swap');
body {
margin: 0px;
overflow: hidden;
font-family: 'Lato', sans-serif;
/* background-image: url(images/GameBackground.png);
background-size: cover;
background-position: center; */
}
img {
height: 300px;
width: 300px;
padding: 200px 0px 150px 0px;
}
button {
background: none;
border: 0;
font-size: 50px;
}
.roll-dice {
padding-bottom: 25px;
}
.gameContainer {
display: flex;
justify-content: space-around;
align-items: center;
align-content: center;
height: 100vh;
width: 100vw;
z-index: 5;
}
#player-background {
position: absolute;
height: 100vh;
width: 50vw;
background: paleturquoise;
z-index: -5;
}
#player1, #player2, #center {
font-size: 50px;
font-weight: 300;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.score {
color: red;
font-size: 100px;
font-weight: 300;
display: flex;
margin-bottom: 250px;
}
.cText {
font-size: 30px;
}
.current {
display: flex;
align-items: center;
justify-content: space-evenly;
background: rgb(226, 226, 187);
height: 125px;
width: 350px;
}
And my JS
//------------------------ Get element variables ---------------
let newGame = document.getElementById("new-game");
let roll = document.getElementById("roll");
let hold = document.getElementById("hold");
let img = document.getElementById("img");
let globalP1 = document.getElementById("globalP1");
let roundP1 = document.getElementById("roundP1");
let globalP2 = document.getElementById("globalP2");
let roundP2 = document.getElementById("roundP2");
let backgroundTarget = document.getElementById("player-background");
//----------------------- Game variables ---------------
let activePlayer = 1;
//----------------------------Anime JS---------------------
let background = anime({
targets: backgroundTarget,
translateX: "50vw",
autoplay: false,
backgroundColor: "#676790",
});
//------------------ Functions used later ---------------
// Switch the player display depending on the situation
function switchPlayer() {
if (activePlayer == 1) {
background.reverse();
player1.style.opacity = "1";
player2.style.opacity = "0.3";
} else {
background.play();
player1.style.opacity = "0.3";
player2.style.opacity = "1";
}
}
let gameReset = () => {
// rewrite everything to "0"
roundP1.textContent = "0";
globalP1.textContent = "0";
roundP2.textContent = "0";
globalP2.textContent = "0";
// Force player one display
activePlayer = 1;
switchPlayer();
};
//--------------------- Main functions ----------------------
// Roll logic
let rollfunction = () => {
// Generates a random number between 1 to 6
let diceNumber = Math.floor(Math.random() * 6) + 1;
// Generate a string with the img path, including a random number between then display the img
let randomImg = "images/" + diceNumber + ".png";
// Used to get the img element then set the src attribute to randomImg (= images/1.png or any other numbers)
document.querySelector("img").setAttribute("src", randomImg);
if (activePlayer == 1 && diceNumber > 1) {
roll.textContent = "ROLL DICE !";
// puts the number into the current score
let totalCurrentP1 = roundP1.textContent;
// I had to put a "+" to make the addition possible beacause rounsP2.textContent is a string, the + is here to convert a string into a number
roundP1.textContent = +totalCurrentP1 + +diceNumber;
switchPlayer();
} else if (activePlayer == 1 && diceNumber == 1) {
// Switch player if dice = 1
roll.textContent = "NEXT PLAYER";
roundP1.textContent = "0";
activePlayer = activePlayer + 1;
switchPlayer();
} else if (activePlayer == 2 && diceNumber > 1) {
// Put score into P2 section
roll.textContent = "ROLL DICE !";
let totalCurrentP2 = roundP2.textContent;
roundP2.textContent = +totalCurrentP2 + +diceNumber;
} else {
// Switch to player 1 if player 2 get "1"
roundP2.textContent = "0";
roll.textContent = "NEXT PLAYER";
activePlayer = activePlayer - 1;
}
};
// Hold ogic
let holdFunction = () => {
let totalGlobalP1 = globalP1.textContent;
let totalGlobalP2 = globalP2.textContent;
let numberRoundP1 = roundP1.textContent;
let numberRoundP2 = roundP2.textContent;
if (activePlayer == 1 && +totalGlobalP1 + +numberRoundP1 < 100) {
globalP1.textContent = +totalGlobalP1 + +roundP1.textContent;
roundP1.textContent = "0";
activePlayer = 2;
switchPlayer();
// background.play();
} else if (activePlayer == 1 && +totalGlobalP1 + +numberRoundP1 >= 100) {
activePlayer = 1;
alert("P1 t'as gagné mon pote !");
gameReset();
// background.reverse();
} else if (activePlayer == 2 && +totalGlobalP2 + +numberRoundP2 < 100) {
globalP2.textContent = +totalGlobalP2 + +roundP2.textContent;
roundP2.textContent = "0";
activePlayer = 1;
switchPlayer();
// background.reverse();
} else if (activePlayer == 2 && +totalGlobalP2 + +numberRoundP2 >= 100) {
activePlayer = 2;
alert(
"Le joueur 2 a gagné cette manche ! (De toute façon c'était mon préféré"
);
gameReset();
}
};
//--------------------- Buttons ----------------------------
// Every buttons holding every functions
hold.addEventListener("click", () => {
holdFunction();
});
roll.addEventListener("click", () => {
rollfunction();
});
newGame.addEventListener("click", () => {
gameReset();
});
Tried :
Looked on stack overflow to see if someone else had this problem
Tried to look more in depth into the anime.js doc
Also tried to do a blank project only with this function but still this issue
Expecting :
Move the div left and right depending on the active player to see who's playing

Creating a number guessing game that has stages

Create a number guessing game to generate a number between the range of 1 and 2. The game should prompt users for their username.
Set range as function parameter and prompt the player to predict the generated number between the given range. At a correct guess, the game should award the player a point and move them to stage 2 by increasing the range limit value by 1, e.g. range is from 1 and 3 for stage 2 and so on.
I created it but to flow from one level to another is the problem
This is a working solution. Feel free to comment with any questions.
let rangeTracker = [1, 2]
let totalPointsTracker = [0]
function randomIntFromInterval(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
document.getElementById('continueBtn').onclick = function() {
let username = document.getElementById("newUsername").value
if (username != '') {
document.querySelector('.usernameCon').style.display = 'none'
document.querySelector('.userInfo').style.display = 'flex'
document.querySelector('.gameCon').style.display = 'flex'
document.getElementById("username").innerHTML = `Hello ${username}, this is stage ${totalPointsTracker[0] + 1}`
document.getElementById("totalPoints").innerHTML = `Total Points: ${totalPointsTracker[0]}`
document.getElementById("title").innerHTML = `Guess a number between ${rangeTracker[0]} and ${rangeTracker[1]}`
}
}
const randomInt = randomIntFromInterval(rangeTracker[0], rangeTracker[1])
document.getElementById('guessBtn').onclick = function() {
let userGuess = document.getElementById("userGuess").value
if (userGuess != '') {
if (userGuess == randomInt) {
document.getElementById("status").innerHTML = `Correct!`
document.getElementById("status").style.color = 'forestgreen'
const previousMaxRange = rangeTracker[1]
rangeTracker[1] = previousMaxRange + 1
const previousTotalPoints = totalPointsTracker[0]
totalPointsTracker[0] = previousTotalPoints + 1
success()
} else {
document.getElementById("userGuess").value = ''
document.getElementById("status").innerHTML = `Incorrect, guess again!`
document.getElementById("status").style.color = 'red'
}
} else if (userGuess == '') {
document.getElementById("status").innerHTML = `Please guess a number!`
document.getElementById("status").style.color = 'red'
}
}
function success() {
let username = document.getElementById("newUsername").value
document.getElementById("userGuess").value = ''
document.getElementById("username").innerHTML = `Hello ${username}, this is stage ${totalPointsTracker[0] + 1}`
document.getElementById("totalPoints").innerHTML = `Total Points: ${totalPointsTracker[0]}`
document.getElementById("title").innerHTML = `Guess a number between ${rangeTracker[0]} and ${rangeTracker[1]}`
}
.usernameCon {
display: flex;
flex-direction: column;
}
.usernameCon label {
font-size: 26px;
}
.usernameCon #newUsername {
width: 230px;
margin-top: 10px;
}
.usernameCon #continueBtn {
font-size: 17px;
width: 130px;
margin-top: 10px;
cursor: pointer;
}
.userInfo {
display: none;
flex-direction: column;
height: fit-content;
}
.userInfo #username {
font-size: 26px;
}
.userInfo #totalPoints {
font-size: 24px;
margin-top: 10px;
}
.gameCon {
display: none;
flex-direction: column;
}
.gameCon #title {
font-size: 22px;
margin-top: 10px;
}
.gameCon #status {
font-size: 20px;
margin-top: 10px;
}
.gameCon #userGuess {
width: 230px;
margin-top: 8px;
}
.gameCon #guessBtn {
font-size: 17px;
width: 130px;
margin-top: 10px;
cursor: pointer;
}
<div class="usernameCon">
<label>Enter Your Username</label>
<input id="newUsername" />
<button id="continueBtn">Continue</buttom>
</div>
<div class="userInfo">
<label id="username"></label>
<label id="totalPoints"></label>
</div>
<div class="gameCon">
<label id="title">Guess a number between 1 and 2</label>
<label id="status"></label>
<input id="userGuess" />
<button id="guessBtn">Guess</button>
</div>

Vanila JS counter

Trying to get all four counters to finish at the same time.
e.g.
Counter four should stay at zero until counter three reaches value of 14, then switch to 1.
Counter one should increment by following counter two and three.
https://jsfiddle.net/IhaveVoicesinMyhead/0j9q1b85/2/#&togetherjs=f3xchiJM9f
const counter = document.querySelectorAll('.counter');
const speed = 200;
counter.forEach(counter => {
const updateCount = () => {
const target = +counter.getAttribute('data-target');
const count = +counter.innerText;
const increment = target / speed;
if (count < target) {
counter.innerText = Math.ceil(count + increment);
setTimeout(updateCount, 80);
} else {
count.innerText = target;
}
}
updateCount();
});
.counters {
margin-top: 2em;
}
.counters .container-max-width {
display: flex;
position: relative;
flex-flow: row nowrap;
justify-content: center;
align-items: stretch;
background-color: #fff;
font-size: 1rem;
text-transform: uppercase;
}
.counters .container-max-width .counter {
font-size: 3.25em;
}
.counters .container-max-width span {
color: #848484;
padding: 0;
}
.counters .container-inner-width {
display: grid;
grid-gap: 200px;
grid-template-columns: repeat(4, 1fr);
padding: 85px 0 85px 0;
text-align: center;
}
<section class="counters">
<div class="container-max-width">
<div class="container-inner-width">
<div>
<div class="counter counter-one" data-target="4">0</div>
<span>counter one</span>
</div>
<div>
<div class="counter counter-two" data-target="10">0</div>
<span>counter two</span>
</div>
<div>
<div class="counter counter-three" data-target="15">0</div>
<span>counter three</span>
</div>
<div>
<div class="counter counter-four" data-target="1">0</div>
<span>counter four</span>
</div>
</div>
</div>
</section>
Here's a simple example of how you can create multiple timers that all end at the same time. I separated the logic behind it (countUp()) from the view (createTimer()), so you should be able to adapt the countUp() function to whatever needs you have. Just give it your desired final value, how long you want it to execute (in ms), and what you want it to do at each step.
const counter1 = document.getElementById('counter1')
const counter2 = document.getElementById('counter1')
function countUp({ to, onStep, duration, _currentValue = 0 }) {
onStep(_currentValue)
if (_currentValue === to) return
setTimeout(() => {
countUp({ to, onStep,duration, _currentValue: _currentValue + 1 })
}, duration / to)
}
function createTimer(to) {
const timerEl = document.createElement('div')
document.body.appendChild(timerEl)
const onStep = currentValue => {
timerEl.innerText = currentValue
}
countUp({ to, duration: 3000, onStep })
}
createTimer(1)
createTimer(10)
createTimer(20)
Note that this doesn't account for setTimeout() drift (setTimeout doesn't always execute the callback as soon as the you asked it to), so you shouldn't use this solution for longer-lasting or very precise timers. You can add some more math to it if you want to account for drift.

TicTacToe - minimax-function always returns -10 (Javascript)

I'm working on a tictactoe Person vs Person game. As a bonus, we can try to implement a minimax algorithm to play against the computer. After numerous trials and errors, I think I have the function so far, that it goes through the calculation without an error. However, the return value for best Score is always -10, assuming the player and not the computer would win.
I have two problems in the code:
the minimax-function always returns the value -10, assuming the person would win.
I cant play the last turn. In the debugger I can see that my gameData array, that keeps track of the visible field, works normally like so: [0,"X","X","O","X","O","O",7,"X"]. But the arrays player.person and player.computer have already stored the 0 and the 7. This should not happen.
At this point I am stuck. And I would tell you what I have been trying so far, but the last couple hours were just poking in the dark pretty much. Therefore the least I can do is write down the steps the code takes till the end:
Important variables etc:
Computer is represented by "O", person by "X"
Array gameData: Starts as [0,1,2,...,8] and replaces the numbers with "X" or "O". It tries to prevent that the tictactoe fields can be selected more than once.
Object player with .person-Array and .computer-Array both start as empty. I use these to check if either of them won the game.
isWinner(PLAYER) iterates through the winning conditions and checks if person or computer match it. The function returns the object ({win:true}) if so.
isTie() checks if there is a tie. It returns the object ({tie:true]), if the conditions isWinner(player.computer) = false, isWinner(player.person) = false and emptySpaces(gameData).length = 0 are met.
the function bestMove() searches for the bestMove of computer. Here's where minimax gets executed.
Code Logic overall:
the code starts at line 140 with function vsComputerGame()
next step is function personMove() in line 301
the player's choice "X" gets stored in the gameData Array. EG gameData = [0,"X",2,...,8]. And the position of "X" (in the exp 1`) gets pushed into player.person.
next it checks, if the person won the game or the game is a tie.
next the function bestMove() gets executed. It chooses a tictactoe-field for the computer.
next player.person and player.computer get updated. Without this step, minimax keeps pushing numbers in those arrays.
Code logic bestMove() and minimax():
bestMove() starts at line 190
the initial player.person and player.computer are saved, to restore them, right after the minimax() function returns a value. Same problem as above: Otherwise, isWinner() returns true on the second click of the player.
for loop gets executed. It selects the first available spot in gameData and replaces it with an "O". player.computer gets updated with this possible move from the computer.
then minimax gets executed, which basically has the same code for both player.computer and player.person, as described in this for loop.
when minimax() returns a value, it get stored in the variable score.
now the gameData Array, player.person and player.computer are reset, so the next iteration of the for loop does not flood them.
at last the if (score.eval > bestScore) checks for the highest score. If highest, the index of the current iteration gets stored in the move variable and is then used to place the "O" on the visible field and inside gameData.
"use strict"
// this function stores the chosen players (condition: player1, player2, computer). The start-game is in another module
let menupage = (function() {
let playerSelection = document.querySelectorAll(".player-selection");
let modalContainer = document.querySelector(".modal-bg");
let submitName = document.querySelector("#submit");
let btnColorPlayerTwo = document.querySelector("#player-two");
let btnColorComputer = document.querySelector("#computer");
let btnColorplayerOne = document.querySelector("#player-one");
let modalClose = document.querySelector(".modal-close");
let inputField = document.querySelector("#name");
let isplayerOne;
let gameModeData = {
playerOne : "",
playerTwo : "",
computer : false,
}
function closeModal() {
inputField.value = "";
modalContainer.classList.remove("bg-active");
}
function submitPlayer() {
if (isplayerOne === true) {
if (inputField.value === "") {
alert("Please enter your battle-tag");
} else if (inputField.value !== "") {
btnColorplayerOne.style.backgroundColor = "#4CAF50";
gameModeData.playerOne = inputField.value;
inputField.value = "";
modalContainer.classList.remove("bg-active");
}
}
if (isplayerOne === false) {
if (inputField.value === "") {
alert("Please enter your battle-tag");
} else if (inputField.value !== "") {
gameModeData.playerTwo = inputField.value;
btnColorPlayerTwo.style.backgroundColor = "#f44336";
gameModeData.computer = false;
btnColorComputer.style.backgroundColor = "#e7e7e7";
inputField.value = "";
modalContainer.classList.remove("bg-active");
}
}
}
function definePlayer(id, color) {
modalClose.addEventListener("click", closeModal);
if (id === "player-one") {
if (color.backgroundColor === "" || color.backgroundColor === "rgb(231, 231, 231)") {
isplayerOne = true;
modalContainer.classList.add("bg-active");
submitName.addEventListener("click", submitPlayer);
} else if (color.backgroundColor === "rgb(76, 175, 80)") {
color.backgroundColor = "#e7e7e7";
gameModeData.playerOne = "";
}
}
if (id === "player-two") {
if (color.backgroundColor === "" || color.backgroundColor === "rgb(231, 231, 231)") {
isplayerOne = false;
modalContainer.classList.add("bg-active");
submitName.addEventListener("click", submitPlayer);
}
}
}
function defineOponent(target) {
if (target.backgroundColor === "rgb(0, 140, 186)") {
return;
} else if (target.backgroundColor === "rgb(231, 231, 231)" || target.backgroundColor === "") {
target.backgroundColor = "#008CBA";
btnColorPlayerTwo.style.backgroundColor = "#e7e7e7";
gameModeData.playerTwo = "";
gameModeData.computer = true;
}
}
let setupPlayers = function setupPlayers() {
if (this.id === "player-one" || this.id === "player-two") {
definePlayer(this.id, this.style);
} else if (this.id === "computer") {
defineOponent(this.style);
}
}
playerSelection.forEach(button => button.addEventListener("click", setupPlayers))
return gameModeData;
}())
let startRound = (function startRound() {
let startGameBtn = document.querySelector("#start-game");
let startScreen = document.querySelector(".start-screen");
let gameboard = document.querySelector(".gameboard");
let selectionMenu = document.querySelector(".window-container");
let frame = document.querySelector(".frame");
let scoreboardPlayer = document.querySelector(".scoreboard-left");
let scoreboardOponent = document.querySelector(".scoreboard-right");
let scorePlayer = document.querySelector(".scoreboard-player");
let scoreOponent = document.querySelector(".scoreboard-oponent");
function displayScore() {
scorePlayer.innerText = menupage.playerOne;
menupage.computer === false ? scoreOponent.innerText = menupage.playerTwo : scoreOponent.innerText = "Computer";
}
function startGame() {
if (menupage.playerOne === "") {
alert("Please choose your profile.");
} else if (menupage.playerTwo === "" && menupage.computer === false) {
alert("Please choose an opponent.")
} else {
startScreen.style.display = "none";
gameboard.style.display = "grid";
scoreboardPlayer.style.display = "grid";
scoreboardOponent.style.display = "grid";
frame.style.display = "none";
selectionMenu.style.gridTemplateAreas = '"header header header" "scoreboard-left gameboard scoreboard-right" "frame frame frame"';
displayScore();
game();
}
}
startGameBtn.addEventListener("click", startGame);
}())
/* ***************************** GAME VS COMPUTER FUNCTION STARTS HERE ************************* */
let vsComputerGame = (function vsComputerGame() {
let player = {
person : [],
computer : [],
}
let gameData = [0, 1, 2, 3, 4, 5, 6, 7, 8]
let isWinner = function isWinner(PLAYER) {
let check = PLAYER.join();
let condition = {
1 : ["0","1","2"],
2 : ["3","4","5"],
3 : ["6","7","8"],
4 : ["0","4","8"],
5 : ["2","4","6"],
6 : ["0","3","6"],
7 : ["1","4","7"],
9 : ["2","5","8"]
}
for (const property in condition) {
if (condition[property].every(v => check.includes(v)) === true) {
return ({win : true});
}
}
return ({win : false });
};
let isTie = function isTie() {
if (emptySpaces(gameData).length === 0 && isWinner(player.computer).win === false && isWinner(player.person).win === false) {
return ({tie: true});
} else {
return ({tie : false});
}
}
function emptySpaces(gameData) {
let updatedBoard = [];
for (let i = 0; i < gameData.length; i++) {
if (gameData[i] !== "X") {
if (gameData[i] !== "O") {
updatedBoard.push(gameData[i]);
}
}
}
return updatedBoard;
}
function bestMove() {
let bestScore = -Infinity;
let move;
// the object player with the values {player:[], computer:[]} is used in isWinner to check who won,
// storedComputer and storedPlayer is needed, to reset both arrays after they go through minimax,
// without, the two object-arrays get fludded
let storedComputer = player.computer.map(x => x);
let storedPlayer = player.person.map(x => x);
// first round of the for loop sets a field for the computer,
// first execution of minimax jumps to player.person
for (let i = 0; i < 9; i++) {
// gameData is the Array, that stores the players' moves. Example: [0, 1, 2, "X", 4, 5, "O", 7, 8]
if (gameData[i] !== "X") {
if (gameData[i] !== "O") {
gameData[i] = "O";
player.computer.push(i);
let score = minimax(gameData, player.person);
gameData[i] = i;
player.person = storedPlayer;
player.computer = storedComputer;
if (score.eval > bestScore) {
bestScore = score.eval;
move = i;
console.log(bestScore);
}
}
}
}
// after a move is found for the computer, O gets logged in the gameData Array and on the visible gameboard
let positionO = document.getElementsByName(move);
gameData[move] = "O";
positionO[0].innerText = "O";
}
function minimax(gameData, PLAYER) {
// the BASE of minimax.
// ************ console.log shows, that it always returns -10 ***************
if (isWinner(player.person).win === true) { return ({eval:-10});}
if (isWinner(player.computer).win === true) { return ({eval:10});}
if (isTie().tie === true) {return ({eval:0});};
/*
PLAYER.push pushes the index-number into either player.computer or player.person
This is needed to check the isWinner function
After that, these Arrays get stored in storedComputer and storedPlayer
*/
if (PLAYER === player.computer) {
let bestScore = -Infinity;
for (let i = 0; i < 9; i++) {
if (gameData[i] !== "X") {
if (gameData[i] !== "O") {
PLAYER.push(i);
//let storedComputer = player.computer.map(x => x);
//let storedPlayer = player.person.map(x => x);
gameData[i] = "O";
let score = minimax(gameData, player.person);
//player.person, player.computer and gameData are resetted, after minimax returns a value
gameData[i] = i;
//player.person = storedPlayer;
//player.computer = storedComputer;
if (score.eval > bestScore) {
bestScore = score.eval;
}
}
}
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < 9; i++) {
if (gameData[i] !== "X") {
if (gameData[i] !== "O") {
PLAYER.push(i);
//let storedComputer = player.computer.map(x => x);
//let storedPlayer = player.person.map(x => x);
gameData[i] = "X";
let score = minimax(gameData, player.computer);
//player.person = storedPlayer;
//player.computer = storedComputer;
gameData[i] = i;
if (score.eval < bestScore) {
bestScore = score.eval;
}
}
}
}
return bestScore;
}
}
let cells = document.querySelectorAll(".cell");
cells.forEach(cell => cell.addEventListener("click", personMove));
function personMove() {
if (this.innerText === "X" || this.innerText === "O") {
return;
} else {
let playersChoice = this.getAttribute("data-parent");
player.person.push(Number(this.getAttribute("data-parent")));
this.innerText = "X";
gameData[playersChoice] = "X";
if (isWinner(player.person).win === true) {
console.log("Win");
};
isTie();
bestMove();
player.computer = [];
player.person = [];
for (let i = 0; i < 9; i++) {
if (gameData[i] === "X") {
player.person.push(i);
} else if (gameData[i] === "O") {
player.computer.push(i);
}
}
}
}
})
/* ***************************** GAME VS COMPUTER FUNCTION ENDS HERE ************************* */
let vsPersonGame = (function vsPersonGame() {
let i = 1;
let player = [];
let oponent = [];
let buttons = document.querySelectorAll(".cell");
buttons.forEach(button => button.addEventListener("click", selection));
function selection() {
let newLocal = this
storeSelection(newLocal);
checkWinner();
}
function storeSelection(input) {
if (i >= 10 || input.innerText !== "") {
return;
} else if (i % 2 === 0) {
return input.innerText = "O", oponent.push(input.dataset.parent),
i++;
} else if (i % 2 !== 0) {
return input.innerText = "X", player.push(input.dataset.parent),
i++;
}
}
function checkWinner() {
let condition = {
1 : ["0","1","2"],
2 : ["3","4","5"],
3 : ["6","7","8"],
4 : ["0","4","8"],
5 : ["2","4","6"],
6 : ["0","3","6"],
7 : ["1","4","7"],
9 : ["2","5","8"]
}
for (const property in condition) {
let toStringplayer = player.join();
let toStringoponent = oponent.join();
if (condition[property].every(v => toStringplayer.includes(v)) === true) {
return alert(menupage.playerOne + " won");
} else if (condition[property].every(v => toStringoponent.includes(v)) === true) {
return alert(menupage.playerTwo + " won");
} else if (i === 10) {
if (condition[property].every(v => toStringplayer.includes(v)) === true) {
return alert(menupage.playerOne + " won");
} else if (condition[property].every(v => toStringoponent.includes(v)) === true) {
return alert(menupage.playerTwo + " won");
} else {
return alert("You tied");
}
}
}
}
})
let game = (function() {
if (menupage.computer === true) {
vsComputerGame();
} else {
vsPersonGame();
}
});
html, body {
display: grid;
height: 100%;
margin: 0;
padding: 0;
}
.window-container {
display: grid;
height: 100%;
grid-template-rows: 10% 80% 10%;
grid-template-areas:
"header header header"
". start-screen ."
"frame frame frame";
}
.header {
grid-area: header;
margin-top: 50px;
font-size: 30px;
text-align: center;
color: red;
font-weight: bolder;
}
.start-screen {
grid-area: start-screen;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 30%;
}
.selection-menu {
flex: 1;
display: grid;
grid-template-rows: 40% 20% 40%;
grid-template-areas:
". . player-two"
"player-one vs ."
". . computer"
}
#player-one {
grid-area: player-one;
background-color: #e7e7e7;
}
#player-two {
grid-area: player-two;
background-color: #e7e7e7;
}
#computer {
grid-area: computer;
background-color: #e7e7e7;
}
#start-game {
display: flex;
cursor: pointer;
border: none;
color: white;
background-color: rgb(37, 36, 36);
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
#vs {
grid-area: vs;
text-align: center;
font-size: 30px;
font-weight: bold;
color: #4CAF50;
}
.player-selection {
cursor: pointer;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
.gameboard {
grid-area: gameboard;
margin-top: 10%;
display: none;
justify-content: center;
grid-template-rows: 150px 150px 150px;
grid-template-columns: 150px 150px 150px;
grid-template-areas:
"tL tM tR"
"mL mM mR"
"bL bM bR";
}
.frame {
grid-area: frame;
display: flex;
position: fixed;
height: 250px;
bottom: 0px;
left: 0px;
right: 0px;
margin-bottom: 0px;
justify-content: center;
align-items: center;
}
.cell {
display: flex;
justify-content: center;
align-items: center;
font-size: 40px;
cursor: pointer;
}
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#tL {
grid-area: tL;
border-bottom: 2px solid black;
border-right: 2px solid black;
}
#tM {
grid-area: tM;
border-bottom: 2px solid black;
border-right: 2px solid black;
}
#tR {
grid-area: tR;
border-bottom: 2px solid black;
}
#mL {
grid-area: mL;
border-bottom: 2px solid black;
border-right: 2px solid black;
}
#mM {
grid-area: mM;
border-bottom: 2px solid black;
border-right: 2px solid black;
}
#mR {
grid-area: mR;
border-bottom: 2px solid black;
}
#bL {
grid-area: bL;
border-right: 2px solid black;
}
#bM {
grid-area: bM;
border-right: 2px solid black;
}
#bR {
grid-area: bR;
}
.modal-bg {
position: fixed;
width: 100%;
height: 100vh;
top: 0;
left: 0;
background-color: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s, opacity 0.5s;
}
.bg-active {
visibility: visible;
opacity: 1;
}
.modal {
position: relative;
background-color: white;
border-radius: 5px 5px 5px 5px;
width: 30%;
height: 20%;
display: flex;
justify-content: space-around;
align-items: center;
flex-direction: column;
}
.modal button {
padding: 10px 50px;
background-color: #2980b9;
color: white;
border: none;
cursor: pointer;
}
.modal-close {
position: absolute;
top: 10px;
right: 10px;
font-weight: bold;
cursor: pointer;
}
#modal-headline {
font-size: 20px;
}
#submit {
margin-top: 5px;
}
.scoreboard-left {
display: none;
}
.scoreboard-player {
grid-area: scoreboard-player;
display: flex;
}
.scoreboard-right {
display: none;
}
.scoreboard-oponent {
grid-area: scoreboard-oponent;
display: flex;
}
<!DOCTYPE html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8">
<link href="styles.css" rel="stylesheet" type="text/css" />
<script src="script.js" defer></script>
<title>Tic Tac Toe</title>
</head>
<body>
<div class="window-container">
<div class="header">Tic Tac Toe</div>
<div class="start-screen">
<div class="selection-menu">
<button class="player-selection" id="player-one">Player One</button>
<button class="player-selection" id="player-two">Player Two</button>
<div id="vs">vs</div>
<button class="player-selection" id="computer">Computer</button>
</div>
</div>
<div class="gameboard">
<div class="cell unselectable" id="tL" data-parent="0" name="0"></div>
<div class="cell unselectable" id="tM" data-parent="1" name="1"></div>
<div class="cell unselectable" id="tR" data-parent="2" name="2"></div>
<div class="cell unselectable" id="mL" data-parent="3" name="3"></div>
<div class="cell unselectable" id="mM" data-parent="4" name="4"></div>
<div class="cell unselectable" id="mR" data-parent="5" name="5"></div>
<div class="cell unselectable" id="bL" data-parent="6" name="6"></div>
<div class="cell unselectable" id="bM" data-parent="7" name="7"></div>
<div class="cell unselectable" id="bR" data-parent="8" name="8"></div>
</div>
<div class="modal-bg">
<div class="modal">
<h2 id="modal-headline">Choose a Name:</h2>
<input type="text" id="name">
<button id="submit" class="submit">Submit</button>
<span class="modal-close">X</span>
</div>
</div>
<div class="frame">
<button id="start-game">Start Game</button>
</div>
<div class="scoreboard-left">
<div class="display-player">
<div class="scoreboard-player"></div>
<div class="p-win">Wins: </div><div class="player-win">0</div>
<div class="p-loss">Losses: </div><div class="player-loss">0</div>
<div class="p-tie">Ties: </div><div class="player-tie">0</div>
</div>
</div>
<div class="scoreboard-right">
<div class="display-oponent">
<div class="scoreboard-oponent"></div>
<div class="o-win">Wins: </div><div class="oponent-win">0</div>
<div class="o-loss">Losses: </div><div class="oponent-loss">0</div>
<div class="o-tie">Ties: </div><div class="oponent-tie">0</div>
</div>
</div>
</div>
</body>
</html>
I appreciate any feedback and hints to solve the problem.

How to calculate remaining time in custom html5 audio player with JavaScript?

I found a good custom HTML5 audio player and successfully redesigned it for my needs. Everything about it is ok, except it didn't show "remaining" time (how much time actually left to track end).
I want to add a calculation of time left without changing the original script, Is it possible?
[
function calculateTotalValue(length) {
var minutes = Math.floor(length / 60),
seconds_int = length - minutes * 60,
seconds_str = seconds_int.toString(),
seconds = seconds_str.substr(0, 2),
time = minutes + ':' + seconds
return time;
}
function calculateCurrentValue(currentTime) {
var current_hour = parseInt(currentTime / 3600) % 24,
current_minute = parseInt(currentTime / 60) % 60,
current_seconds_long = currentTime % 60,
current_seconds = current_seconds_long.toFixed(),
current_time = (current_minute < 10 ? "0" + current_minute : current_minute) + ":" + (current_seconds < 10 ? "0" + current_seconds : current_seconds);
return current_time;
}
CodePen (original player source code)
What you want is extremely easy, You don't have to change a lot of code but just need to add just two lines.
Inside the "initProgressBar()" function just add these two lines of code
//calculate the remaining time
var rem_time=length-current_time;
jQuery(".rem-time").html(calculateCurrentValue(rem_time));
//HTML to show the remaining time(add it to the HTML)
<small style="float: left; position: relative; left: 15px;" class="rem-time"></small>
the first line is just getting the total seconds, "length" and "current_time" was already available in the code so I am just using them, nothing new.
In the second line, I am converting the remaining time to a readable format, again using an already available function "calculateCurrentValue()" and setting an HTML element with remaining time.
At last the HTML element in which the remaining time will be shown.
You can also take a look at this CodePen which I have done for a better understanding.
Adding Full code here to make sure it is available always
function calculateTotalValue(length) {
var minutes = Math.floor(length / 60),
seconds_int = length - minutes * 60,
seconds_str = seconds_int.toString(),
seconds = seconds_str.substr(0, 2),
time = minutes + ':' + seconds
return time;
}
function calculateCurrentValue(currentTime) {
var current_hour = parseInt(currentTime / 3600) % 24,
current_minute = parseInt(currentTime / 60) % 60,
current_seconds_long = currentTime % 60,
current_seconds = current_seconds_long.toFixed(),
current_time = (current_minute < 10 ? "0" + current_minute : current_minute) + ":" + (current_seconds < 10 ? "0" + current_seconds : current_seconds);
return current_time;
}
var prevcurrentime=0;
function initProgressBar() {
var player = document.getElementById('player');
var length = player.duration
var current_time = player.currentTime;
// calculate total length of value
var totalLength = calculateTotalValue(length)
jQuery(".end-time").html(totalLength);
// calculate current value time
var currentTime = calculateCurrentValue(current_time);
jQuery(".start-time").html(currentTime);
//checking if the current time is bigger than the previous or else there will be sync different between remaining and current
if(currentTime>prevcurrentime){
//calculate the remaining time
var rem_time=length-current_time;
jQuery(".rem-time").html(calculateCurrentValue(rem_time));
}
//setting the previouscurrent time to this current time
prevcurrentime=currentTime;
//progress bar calculation
var progressbar = document.getElementById('seekObj');
progressbar.value = (player.currentTime / player.duration);
progressbar.addEventListener("click", seek);
if (player.currentTime == player.duration) {
$('#play-btn').removeClass('pause');
}
function seek(evt) {
var percent = evt.offsetX / this.offsetWidth;
player.currentTime = percent * player.duration;
progressbar.value = percent / 100;
}
};
function initPlayers(num) {
// pass num in if there are multiple audio players e.g 'player' + i
for (var i = 0; i < num; i++) {
(function() {
// Variables
// ----------------------------------------------------------
// audio embed object
var playerContainer = document.getElementById('player-container'),
player = document.getElementById('player'),
isPlaying = false,
playBtn = document.getElementById('play-btn');
// Controls Listeners
// ----------------------------------------------------------
if (playBtn != null) {
playBtn.addEventListener('click', function() {
togglePlay()
});
}
// Controls & Sounds Methods
// ----------------------------------------------------------
function togglePlay() {
if (player.paused === false) {
player.pause();
isPlaying = false;
$('#play-btn').removeClass('pause');
} else {
player.play();
$('#play-btn').addClass('pause');
isPlaying = true;
}
}
}());
}
}
initPlayers(jQuery('#player-container').length);
html {
height: 100%;
display: table;
margin: auto;
}
body {
height: 100%;
display: table-cell;
vertical-align: middle;
background: yellow;
}
.audio-player {
background: white;
border: 1px solid #dfdfdf;
width: 50vw;
text-align: center;
display: flex;
flex-flow: row;
margin: 4rem 0 4rem 0;
}
.audio-player .album-image {
min-height: 100px;
width: 110px;
background-size: cover;
}
.audio-player .player-controls {
align-items: center;
justify-content: center;
margin-top: 2.5rem;
flex: 3;
}
.audio-player .player-controls progress {
width: 90%;
}
.audio-player .player-controls progress[value] {
-webkit-appearance: none;
appearance: none;
background-color: white;
color: blue;
height: 5px;
}
.audio-player .player-controls progress[value]::-webkit-progress-bar {
background-color: white;
border-radius: 2px;
border: 1px solid #dfdfdf;
color: blue;
}
.audio-player .player-controls progress::-webkit-progress-value {
background-color: blue;
}
.audio-player .player-controls p {
font-size: 1.6rem;
}
.audio-player #play-btn {
background-image: url("http://www.lukeduncan.me/images/play-button.png");
background-size: cover;
width: 75px;
height: 75px;
margin: 2rem 0 2rem 2rem;
}
.audio-player #play-btn.pause {
background-image: url("http://www.lukeduncan.me/images/pause-button.png");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="audio-player">
<div id="play-btn"></div>
<div class="audio-wrapper" id="player-container" href="javascript:;">
<audio id="player" ontimeupdate="initProgressBar()">
<source src="http://www.lukeduncan.me/oslo.mp3" type="audio/mp3">
</audio>
</div>
<div class="player-controls scrubber">
<p>Oslo <small>by</small> Holy Esque</p>
<span id="seekObjContainer">
<progress id="seekObj" value="0" max="1"></progress>
</span>
<br>
<small style="float: left; position: relative; left: 15px;" class="start-time"></small><br/>
<small style="float: left; position: relative; left: 15px;" class="rem-time"></small>
<small style="float: right; position: relative; right: 20px;" class="end-time"></small>
</div>
<div class="album-image" style="background-image: url('https://artwork-cdn.7static.com/static/img/sleeveart/00/051/614/0005161476_350.jpg')"></div>
</div>

Categories

Resources