i'm currently working through Udemy's Vue.js tutorial. I've reached the section where you are building a battle web app game. After finishing it, I decided to practice my refactoring and came across this bug.
When you click the attack buttons and then the confirm box comes up to ask if you want to play again, it seems to add one extra item in my log array instead of resetting the game fully.
I'm suspecting it is to do with pressing the attack buttons too quickly, and then the confirm box comes up before running an addToLog() and then it runs it afterwards.
Or it could be my bad code. lol
Note that I know that clicking cancel on the confirm box also comes up with bugs too.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Monster Slayer</title>
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<link rel="stylesheet" href="css/foundation.min.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div id="app">
<section class="row">
<div class="small-6 columns">
<h1 class="text-center">YOU</h1>
<div class="healthbar">
<div class="healthbar text-center" style="background-color: green; margin: 0; color: white;" :style="{width: playerHealth + '%'}">
{{ playerHealth }}
</div>
</div>
</div>
<div class="small-6 columns">
<h1 class="text-center">BADDY</h1>
<div class="healthbar">
<div class="healthbar text-center" style="background-color: green; margin: 0; color: white;" :style="{width: computerHealth + '%'}">
{{ computerHealth }}
</div>
</div>
</div>
</section>
<section class="row controls" v-if="!isRunning">
<div class="small-12 columns">
<button id="start-game" #click="startGame">START GAME</button>
</div>
</section>
<section class="row controls" v-else>
<div class="small-12 columns">
<button id="attack" #click="attack">ATTACK</button>
<button id="special-attack" #click="specialAttack">SPECIAL ATTACK</button>
<button id="heal" #click="heal">HEAL</button>
<button id="restart" #click="restart">RESTART</button>
</div>
</section>
<section class="row log" v-if="turns.length > 0">
<div class="small-12 columns">
<ul>
<li v-for="turn in turns" :class="{'player-turn': turn.isPlayer, 'monster-turn': !turn.isPlayer}">
{{ turn.text }}
</li>
</ul>
</div>
</section>
</div>
<script src="app.js"></script>
</body>
</html>
css/app.css
.text-center {
text-align: center;
}
.healthbar {
width: 80%;
height: 40px;
background-color: #eee;
margin: auto;
transition: width 500ms;
}
.controls,
.log {
margin-top: 30px;
text-align: center;
padding: 10px;
border: 1px solid #ccc;
box-shadow: 0px 3px 6px #ccc;
}
.turn {
margin-top: 20px;
margin-bottom: 20px;
font-weight: bold;
font-size: 22px;
}
.log ul {
list-style: none;
font-weight: bold;
text-transform: uppercase;
}
.log ul li {
margin: 5px;
}
.log ul .player-turn {
color: blue;
background-color: #e4e8ff;
}
.log ul .monster-turn {
color: red;
background-color: #ffc0c1;
}
button {
font-size: 20px;
background-color: #eee;
padding: 12px;
box-shadow: 0 1px 1px black;
margin: 10px;
}
#start-game {
background-color: #aaffb0;
}
#start-game:hover {
background-color: #76ff7e;
}
#attack {
background-color: #ff7367;
}
#attack:hover {
background-color: #ff3f43;
}
#special-attack {
background-color: #ffaf4f;
}
#special-attack:hover {
background-color: #ff9a2b;
}
#heal {
background-color: #aaffb0;
}
#heal:hover {
background-color: #76ff7e;
}
#restart {
background-color: #ffffff;
}
#restart:hover {
background-color: #c7c7c7;
}
app.js
new Vue({
el: app,
data: {
playerHealth: 100,
computerHealth: 100,
isRunning: false,
turns: [],
},
methods: {
startGame: function() {
this.isRunning = true;
this.playerHealth = 100;
this.computerHealth = 100;
this.clearLog();
},
attackController: function(attacker, maxRange, minRange) {
let receiver = this.setReceiver(attacker);
let damage = 0;
if (attacker === 'player') {
damage = this.randomDamage(maxRange, minRange);
this.computerHealth -= damage;
}
if (attacker === 'computer') {
damage = this.randomDamage(maxRange, minRange);
this.playerHealth -= damage;
}
this.addToLog(attacker, receiver, damage);
if (this.checkWin()) {
return;
}
},
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
},
specialAttack: function() {
this.attackController('player', 30, 5);
this.attackController('computer', 30, 5);
},
heal: function() {
if (this.playerHealth <= 90) {
this.playerHealth += 10;
} else {
this.playerHealth = 100;
}
this.turns.unshift({
isPlayer: true,
text: 'Player heals for ' + 10,
});
},
randomDamage: function(max, min) {
return Math.floor(Math.random() * max, min);
},
checkWin: function() {
if (this.computerHealth <= 0) {
this.alertBox('YOU WIN! New Game?');
} else if (this.playerHealth <= 0) {
this.alertBox('LOSER!!! New Game?');
}
return false;
},
alertBox: function(message) {
if (confirm(message)) {
this.isRunning = false;
this.startGame();
} else {
this.isRunning = false;
}
return true;
},
restart: function() {
this.isRunning = false;
this.startGame();
},
addToLog: function(attacker, receiver, damage) {
this.turns.unshift({
isPlayer: attacker === 'player',
text: attacker + ' hits ' + receiver + ' for ' + damage,
});
},
clearLog: function() {
this.turns = [];
},
setReceiver: function(attacker) {
if (attacker === 'player') {
return 'computer';
} else {
return 'player';
}
},
damageOutput: function(attacker, health) {
if (attacker === 'player') {
damage = this.randomDamage(maxRange, minRange);
this.computerHealth -= damage;
}
},
},
});
Github repo is here if you prefer that. Thanks!
Your attack (and specialAttack) function attacks for both players:
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
},
Currently, it is checking for win at every attackController call. So when the first attacker (player) wins, the game resets AND the second player attacks.
So, my suggestion, move the checkWin out of the attackController into the attack functions:
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
this.checkWin();
},
The same to specialAttack.
Code/JSFiddle: https://jsfiddle.net/acdcjunior/wwc1xnyc/10/
Note, when the player wins, in the code above, the computer will still "strike back", even though the game is over. If you want to halt that, make checkWin return if the game is over:
checkWin: function() {
if (this.computerHealth <= 0) {
this.alertBox('YOU WIN! New Game?');
return true;
} else if (this.playerHealth <= 0) {
this.alertBox('LOSER!!! New Game?');
return true;
}
return false;
},
And add an if to attack (and specialAttack):
attack: function() {
this.attackController('player', 10, 3);
if (this.checkWin()) return;
this.attackController('computer', 10, 3);
this.checkWin();
},
Updated fiddle: https://jsfiddle.net/acdcjunior/wwc1xnyc/13/
Related
This is an excercise creating a replica of the Simon Game. The program randomly choses an increasing sequence of colors that the player must remember and replicate.
The first game works perfectly as far as I saw, but after failing the first the second game doesn't work. I thouroughly parsed the code using devtools and found that in the second game everything runs perfectly, when pressing the correct button. The nested if statements are fulfilled but when arrive to calling the function (setTimeout(nextRandomSequence,1500);) instead of executing the function (which works in the first game) it jumps back to the top of the click event replicating the button pressing. This duplicate the pressing in the variable gamePattern that then fails to pass the if test (equality between gamePattern and userClickedPattern arrays).
Why?
let userClickedPattern = []
let gamePattern = []
var randomChosenColor = []
let buttonColors = ["green", "red", "yellow", "blue"]
let level = 1
if (level === 1) {
// Press key to start and change title to level 1
$(document).keypress(function() {
$("#level-title").text("Level " + level);
// Select first color in game pattern
nextRandomSequence();
//Click event listener
$("div[type='button']")
.click(function sequence(e) {
let userChosenColor = e.target.id
console.log("user chosen color: " + userChosenColor);
pressButtonAnimation(userChosenColor);
playSound(userChosenColor);
userClickedPattern.push(userChosenColor);
console.log("compare arrays userClicked/gamePattern " + userClickedPattern + " " + gamePattern);
if (userClickedPattern[userClickedPattern.length - 1] === gamePattern[userClickedPattern.length - 1]) {
if (userClickedPattern.length === gamePattern.length) {
$("#level-title").text("Level " + level);
console.log(level);
console.log("----- continue ---------");
setTimeout(nextRandomSequence, 1500);
}
} else {
console.log("----- game over ---------");
playSound("wrong");
gameOverAnimation(userChosenColor);
$("#level-title").text("GAME OVER, your arrived to level " + level + " press any key to start");
restartGame();
console.log(level);
}
});
});
}
// select next color of game pattern. Push chosen color in gamePattern
function nextRandomSequence() {
console.log("--------- inside nextRandomSequence -------------")
userClickedPattern = []
randomNumber = Math.floor(Math.random() * 4);
var randomChosenColor = buttonColors[randomNumber]
gamePattern.push(randomChosenColor);
level++;
$("#" + randomChosenColor).fadeIn(300).fadeOut(300).fadeIn(300);
playSound(randomChosenColor);
console.log("random chosen color " + randomChosenColor);
}
function playSound(name) {
var audio = new Audio("sounds/" + name + ".mp3");
audio.play();
}
function pressButtonAnimation(colorPressed) {
$("#" + colorPressed).addClass("pressed");
setTimeout(function() {
$("#" + colorPressed).removeClass("pressed");
}, 100)
}
function gameOverAnimation(colorPressed) {
$("body").addClass("game-over");
setTimeout(function() {
$("body").removeClass("game-over");
}, 200)
}
function restartGame() {
level = 1
gamePattern = []
}
body {
text-align: center;
background-color: #011F3F;
}
#level-title {
font-family: 'Press Start 2P', cursive;
font-size: 3rem;
margin: 5%;
color: #FEF2BF;
}
.container {
display: block;
width: 50%;
margin: auto;
}
.btn {
margin: 25px;
display: inline-block;
height: 200px;
width: 200px;
border: 10px solid black;
border-radius: 20%;
}
.game-over {
background-color: red;
opacity: 0.8;
}
.red {
background-color: red;
}
.green {
background-color: green;
}
.blue {
background-color: blue;
}
.yellow {
background-color: yellow;
}
.pressed {
box-shadow: 0 0 20px white;
background-color: grey;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Simon</title>
<link rel="stylesheet" href="styles.css">
<link href="https://fonts.googleapis.com/css?family=Press+Start+2P" rel="stylesheet">
</head>
<body>
<h1 id="level-title">Press a Key to Start</h1>
<div class="container">
<div lass="row">
<div type="button" id="green" class="btn green">
</div>
<div type="button" id="red" class="btn red">
</div>
</div>
<div class="row">
<div type="button" id="yellow" class="btn yellow">
</div>
<div type="button" id="blue" class="btn blue">
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="game.js" charset="utf-8"></script>
</body>
</html>
I am building a calculator to help practice learning Vue.js 3 (I am new to vue). I have got the basic functionalities down but I am trying to figure out how to add a hover animation over the buttons. If possible I am trying to make a different hover color between the buttons in white and buttons in orange. Any help would be appreciated
Code:
<div class="calculator">
<div class="display">{{ current || '0'}}</div>
<div #click="clear" class="btn">C</div>
<div #click="sign" class="btn">+/-</div>
<div #click="percent" class="btn">%</div>
<div #click="divide" class="operator">รท</div>
<div #click="append('7')" class="btn">7</div>
<div #click="append('8')" class="btn">8</div>
<div #click="append('9')" class="btn">9</div>
<div #click="multiply" class="operator">x</div>
<div #click="append('4')" class="btn">4</div>
<div #click="append('5')" class="btn">5</div>
<div #click="append('6')" class="btn">6</div>
<div #click="minus" class="operator">-</div>
<div #click="append('1')" class="btn">1</div>
<div #click="append('2')" class="btn">2</div>
<div #click="append('3')" class="btn">3</div>
<div #click="plus" class="operator">+</div>
<div #click="append('0')" class="zero">0</div>
<div #click="dot" class="btn">.</div>
<div #click="equal" class="operator">=</div>
</div>
</template>
<script>
export default {
data() {
return {
previous: null,
current: '',
operator: null,
operatorClicked: false,
hover: false
}
},
methods: {
clear() {
this.current = '';
},
sign() {
this.current = this.current.charAt(0) === '-' ?
this.current.slice(1) : `-${this.current}`;
},
percent() {
this.current = `${parseFloat(this.current) / 100}`;
},
append(number) {
if (this.operatorClicked) {
this.current = '';
this.operatorClicked = false;
}
this.current = `${this.current}${number}`;
},
dot() {
if (this.current.indexOf('.') === -1) {
this.append('.')
}
},
setPrevious() {
this.previous = this.current;
this.operatorClicked = true;
},
plus() {
this.operator = (a,b) => a + b;
this.setPrevious();
},
minus() {
this.operator = (a,b) => a - b;
this.setPrevious();
},
multiply() {
this.operator = (a,b) => a * b;
this.setPrevious();
},
divide() {
this.operator = (a,b) => a / b;
this.setPrevious();
},
equal() {
this.current = `${this.operator(
parseFloat(this.current),
parseFloat(this.previous)
)}`;
this.previous = null;
}
}
}
</script>
<style scoped>
.calculator {
margin: 0 auto;
width: 400px;
font-size: 40px;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: minmax(50px, auto);
}
.display {
grid-column: 1 / 5;
background-color: black;
color: white;
}
.zero {
grid-column: 1 / 3;
border: 1px solid black;
}
.btn {
background-color: white;
border: 1px solid black;
}
.operator {
background-color: orange;
color: white;
border: 1px solid black;
}
</style>
You can use the :hover selector pseudo class, no need to involve js/vue for that
ie:
.btn:hover {
background-color: peach;
}
.operator:hover {
background-color: lavender;
}
Yes, just with hover on btns you can achieve this, no need vue or js
.btn:hover {
background-color: #cac8c3;
}
.operator:hover {
background-color: #6f4d00;
}
Exmaple in this codepen https://codepen.io/JavierSR/pen/LYQdjwY
I made a TicTacToe game that happily works. I'm trying to solve two things though.
The opponent's move in "DumbAI" shows immediately after I choose mine. When I impose a setTimeout(), and the AI opponent wins, the endgame sequence does not fire. It works when I win though.
The endgame sequence is that when anyone gets 3 in a row, an alert is supposed to flash, the 3 squares that won are highlighted and the eventlistener is removed so no more marks can be made.
Instead, the code lets me swap to the active player. And if the active player gets 3 in a row, the endgame sequence fires.
All these functions are in the same block. By putting a setTimeout() on the opponent's move, is it skipping over the endgame sequence?
Similarly, when I break out the endgame sequence into a separate block, another issue occurs.
When I take the endgame sequence out of the block and I win, the code will flash the alert and highlight the spaces, but it will also allow the AI opponent to make an extra move.
By taking the endgame sequence out of the block, is the computer moving too quickly through the code by allowing opponent to take his turn before firing the endgame sequence?
script.js:
var ONE_CLASS
var TWO_CLASS
const btn = document.querySelector('#PlayerOneSymbol');
btn.onclick = function () {
const XOs = document.querySelectorAll('input[name="choice"]');
for (const XO of XOs) {
if (XO.checked) {
ONE_CLASS = XO.value
TWO_CLASS = XO.value == 'X' ? 'O' : 'X'
break;
}
}
alert("First Move Belongs to " + ONE_CLASS + ". Select Player Two.");
};
var playerTwoIdentity
const btn2 = document.querySelector('#PlayerTwoChoice');
btn2.onclick = function () {
const Opponents = document.querySelectorAll('input[name="choice2"]');
for (const Opponent of Opponents) {
if (Opponent.checked) {
playerTwoIdentity = Opponent.value
break;
}
}
alert("Your Opponent is " + playerTwoIdentity + ". Start New Game.")
};
let playerOneTurn
function swapTurns() {
playerOneTurn = !playerOneTurn
};
const winningTrios = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[6, 4, 2]
]
restartBtn.addEventListener('click', startGame);
function startGame() {
if (ONE_CLASS == undefined || playerTwoIdentity == undefined) {return alert ("Make sure players are defined")}
console.log("player 1 = " + ONE_CLASS + ", player 2 = " + playerTwoIdentity)
drawBoard();
playerOneTurn = true;
}
const arrayfromBoxes = Array.from(document.getElementsByClassName('box'));
const stylingOfBoxes = document.querySelectorAll('.box');
function drawBoard() {
console.log(stylingOfBoxes)
for (let i = 0; i < stylingOfBoxes.length; i++) {
stylingOfBoxes[i].addEventListener('click', boxmarked, {once: true});}
stylingOfBoxes.forEach(gridBox => {
gridBox.classList.remove(ONE_CLASS)
gridBox.classList.remove(TWO_CLASS)
gridBox.classList.remove('winner')
gridBox.innerHTML = ""
})
}
function boxmarked(e) {
const index = arrayfromBoxes.indexOf(e.target)
// how to consolidate? maybe I just let ONE_CLASS mark and then if the AI or player
// or do it even earlier and link it with playerTurn?
if(playerOneTurn) {
arrayfromBoxes[index].classList.add(ONE_CLASS)
e.target.innerHTML = ONE_CLASS
} else {
arrayfromBoxes[index].classList.add(TWO_CLASS)
e.target.innerHTML = TWO_CLASS
}
// if (playerhasWon()) {
// declareWinner()
// return
// }
// if (emptySpaceRemains() == false) {
// declareTie()
// return
// }
hasGameEnded()
swapTurns()
// eliminate repetition -
if(playerTwoIdentity === "Dumb AI") {
var dumbAIArray = arrayfromBoxes.reduce((dumbAIArray, box, idx) => {
if (box.innerHTML === "") {
dumbAIArray.push(idx);
}
return dumbAIArray;
}, []);
let dumbAIpicked = dumbAIArray[Math.floor(dumbAIArray.length * (Math.random()))]
arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)
arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS
// why does Timeoutfunction prevent opponent sequence?
// setTimeout(() => {arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)}, 500);
// setTimeout(() => {arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS}, 500);
// if (playerhasWon()) {
// declareWinner()
// return
// }
// if (emptySpaceRemains() == false) {
// declareTie()
// return
// }
hasGameEnded()
swapTurns()
} else { console.log("Human")
}
}
function hasGameEnded() {
// fix declareWinner() appears before the added classes bc alert happens quicker than redraw
// I also cannot pull these out because then the opponent move fires and shows
// could have something to do with timing of in-block code
if (playerhasWon()) {
declareWinner()
return
}
if (emptySpaceRemains() == false) {
declareTie()
return
}
}
function checkClass() {
if(playerOneTurn) {
return ONE_CLASS
} else {
return TWO_CLASS
};}
function emptySpaceRemains() {
var innerHTMLempty = (insidebox) => insidebox.innerHTML===""
console.log(arrayfromBoxes.some(innerHTMLempty))
return (arrayfromBoxes.some(innerHTMLempty))
}
function declareTie() {
setTimeout(alert ("TIE GAME"), 1000)}
function playerhasWon() {
var indexOfSelected = arrayfromBoxes.reduce((indexOfSelected, box, idx) => {
if (box.classList[1] === checkClass()) {
indexOfSelected.push(idx);
}
return indexOfSelected;
}, []);
const winningThreeIndexes = winningTrios
.map(trio => trio.filter(i => indexOfSelected.includes(i)))
.filter(i => i.length === 3);
console.log(winningThreeIndexes)
console.log(winningThreeIndexes.length)
if (winningThreeIndexes.length === 1) {winningThreeIndexes[0].map((index) => {arrayfromBoxes[index].className += ' winner'})}
var isThereAWinner =
winningTrios.some(trio => {return trio.every(i => indexOfSelected.includes(i))});
console.log({isThereAWinner});
return isThereAWinner
}
function declareWinner() {
setTimeout(alert (checkClass() + " WINS"), 1000);
for (let i=0; i < stylingOfBoxes.length; i++) {
stylingOfBoxes[i].removeEventListener('click', boxmarked, {once: true});}
}
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tic Tac Toe</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1 id="playtext">Let's Play</h1>
<div class="radioContainer">
<div id="playerOne">
<h3>Player One</h3>
<form>
<input type="radio" name="choice" value="X"> X<br>
<input type="radio" name="choice" value="O"> O<br>
<input type="button" id="PlayerOneSymbol" value="Confirm">
</form>
</div>
<div id="playerTwo">
<h3>Player Two</h3>
<form>
<input type="radio" name="choice2" value="Human"> Human<br>
<input type="radio" name="choice2" value="Dumb AI"> Dumb AI<br>
<input type="radio" name="choice2" value="Smart AI"> Smart AI<br>
<input type="button" id="PlayerTwoChoice" value="Confirm">
</form>
</div>
</div>
<div class="buttonHolder">
<div class="buttonWrapper">
<button id="restartBtn">Start New Game</button>
</div>
</div>
<div class="gameboard">
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
style.css:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
}
#playtext {
text-align: center;
padding: 10px;
}
.buttonHolder {
height: 60px;
width: 100%;
float: left;
position: relative;
background-color: purple;
}
.buttonWrapper {
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.container {
background-color: purple;
justify-content: center;
/* display: flex;
flex-wrap: wrap; */
width: 400px;
height: 600px;
}
#gameboard {
border-top:10px;
border-bottom: 4px;
border-bottom-color: black;
background-color: chartreuse;
}
.box {
background-color: yellow;
width: 125px;
height: 125px;
float: left;
width: 33.33%;
}
button:hover {
cursor: pointer;
transform: translateY(-2px);
}
.winner {
background-color: black;
}
.X {
content: 'X';
font-size: 135px;
}
.O {
content: 'O';
font-size: 135px;
}
#spacer {
height: 10px;
width: 100%;
background-color: purple;
padding: 10px;
}
#playerOne {
background-color: blanchedalmond;
padding: 5px;
height: 110px;
float: left;
width: 50%;
}
#playerTwo {
background-color: mintcream;
padding: 5px;
height: 110px;
float: left;
width: 50%;
}
A friend helped me figure out what's happening in #1 -- I think. He points out that JS is asynchronous. I had three functions:
Opponent puts down marker in a space.
Board is evaluated to see if opponent won.
If not, it switches turns and lets player pick a space.
If so, it ends the game and prevents picking a space
I was hoping when (1) was delayed, (2) and (3) wouldn't fire until (1) did.
But in reality (1) is delayed, so (2) runs anyway and doesn't see the opponent win and so (3) lets player pick a space.
So to fix this, I put the timeout on all 3 functions:
setTimeout(() => {
let dumbAIpicked = dumbAIArray[Math.floor(dumbAIArray.length * (Math.random()))]
arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)
arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS
if (playerhasWon()) {
declareWinner()
return
}
if (emptySpaceRemains() == false) {
declareTie()
return
}
// hasGameEnded()
swapTurns()
``}, 500);
New to JavaScript and Vue.js I'm building a rock paper scissors game and I'm struggling to get it to end after either the player or the computer wins. Right now the game will continue and the progress bar does as well but I need to get it to stop after 10 wins. Not sure how to do this in Vue.js. I would appreciate any pointers.
new Vue({
el: '#app',
data: {
playerWins: 0,
cpuWins: 0,
gameIsRunning: false,
turns: [],
rock: 1,
paper: 2,
scissors: 3
},
methods: {
startGame: function () {
this.gameIsRunning = true;
},
rockUser: function () {
let cpuChoice = this.calculateCpuChoice();
if (cpuChoice === this.paper) {
this.cpuWins += 1;
this.turns.unshift({
isPlayer: false,
text: 'Computer Chose Paper | Paper Beats Rock | Computer Wins!'
})
} else if (cpuChoice === this.scissors) {
this.playerWins += 1;
this.turns.unshift({
isPlayer: true,
text: 'Computer Chose Scissors | Rock Beats Scissors | Player Wins!'
})
} else {
this.turns.unshift({
tie: true,
text: 'Computer Chose Rock | You Have Tied!'
})
}
console.log(this.playerWins);
console.log(this.cpuWins);
},
paperUser: function () {
let cpuChoice = this.calculateCpuChoice();
if (cpuChoice === this.scissors) {
this.cpuWins += 1;
this.turns.unshift({
isPlayer: false,
text: 'Computer Chose Scissors | Scissors Beats Paper | Computer Wins!'
})
} else if (cpuChoice === this.rock) {
this.playerWins += 1;
this.turns.unshift({
isPlayer: true,
text: 'Computer Chose Rock | Paper Beats Rock | Player Wins!'
})
} else {
this.turns.unshift({
tie: true,
text: 'Computer Chose Paper | You Have Tied!'
})
}
console.log(this.playerWins);
console.log(this.cpuWins);
},
scissorsUser: function () {
let cpuChoice = this.calculateCpuChoice();
if (cpuChoice === this.rock) {
this.cpuWins += 1;
this.turns.unshift({
isPlayer: false,
text: 'Computer Chose Rock | Rock Beats Scissors | Computer Wins!'
})
} else if (cpuChoice === this.paper) {
this.playerWins += 1;
this.turns.unshift({
isPlayer: true,
text: 'Computer Chose Paper | Scissors Beats Paper | Player Wins!'
})
} else {
this.turns.unshift({
tie: true,
text: 'Computer Chose Scissors | You Have Tied!'
})
}
console.log(this.playerWins);
console.log(this.cpuWins);
},
restart: function () {
this.gameIsRunning = false;
this.cpuWins = 0;
this.playerWins = 0;
this.turns = [];
},
whoWins: function (playerWins, cpuWins) {
if(cpuWins === 10) {
prompt ("The computer has won!");
this.gameIsRunning = false;
}
if(playerWins === 10) {
prompt ("The player has won!");
this.gameIsRunning = false;
}
},
calculateCpuChoice: function () {
let max = 3;
let min = 0;
return Math.max(Math.floor(Math.random() * max) + 1, min);
}
}
});
.text-center {
text-align: center;
}
.wins {
width: 80%;
color: black;
height: 40px;
background-color: #eee;
margin: auto;
transition: width 1000ms;
}
.controls, .log {
margin-top: 30px;
text-align: center;
padding: 10px;
border: 1px solid #ccc;
box-shadow: 0px 3px 6px #ccc;
}
.turn {
margin-top: 20px;
margin-bottom: 20px;
font-weight: bold;
font-size: 22px;
}
.log ul {
list-style: none;
font-weight: bold;
text-transform: uppercase;
}
.log ul li {
margin: 5px;
}
.log ul .player-turn {
color: green;
background-color: #aaffb0;
}
.log ul .computer-turn {
color: red;
background-color: #ffc0c1;
}
.log ul .tie-turn {
color: blue;
background-color: #e4e8ff;
}
button {
font-size: 20px;
background-color: #eee;
padding: 12px;
box-shadow: 0 1px 1px black;
margin: 10px;
}
#start-game {
background-color: #e4e8ff;
}
#start-game:hover {
background-color: #687eff;
}
#rock {
background-color: #ff7367;
}
#rock:hover {
background-color: #ff3f43;
}
#paper {
background-color: #ffaf4f;
}
#paper:hover {
background-color: #ff9a2b;
}
#scissors {
background-color: #aaffb0;
}
#scissors:hover {
background-color: #76ff7e;
}
#restart {
background-color: #ffffff;
}
#restart:hover {
background-color: #c7c7c7;
}
<!DOCTYPE html>
<html>
<head>
<title>RPS with VueJS</title>
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<link rel="stylesheet" href="css/foundation.min.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div id="app">
<section class="row">
<div class="small-6 columns">
<h1 class="text-center">PLAYER</h1>
<div class="wins">
<div class="wins text-center" style="background-color: green; margin: 0; color: white;"
:style="{ width: playerWins + '0%' }">
{{ playerWins }}
</div>
</div>
</div>
<div class="small-6 columns">
<h1 class="text-center">COMPUTER</h1>
<div class="wins">
<div class="wins text-center" style="background-color: green; margin: 0; color: white;"
:style="{ width: cpuWins + '0%' }">
{{ cpuWins }}
</div>
</div>
</div>
</section>
<section class="row controls" v-if="!gameIsRunning">
<div class="small-12 columns">
<button id="start-game" #click="startGame">START NEW GAME</button>
</div>
</section>
<section class="row controls" v-else>
<div class="small-12 columns">
<button id="rock" #click="rockUser">ROCK</button>
<button id="paper" #click="paperUser">PAPER</button>
<button id="scissors" #click="scissorsUser">SCISSORS</button>
<button id="restart" #click="restart">RESTART</button>
</div>
</section>
<section class="row log" v-if="turns.length > 0">
<div class="small-12 columns">
<ul>
<li v-for="turn in turns"
:class="{'player-turn': turn.isPlayer, 'computer-turn': !turn.isPlayer, 'tie-turn': turn.tie}">
{{ turn.text }}
</li>
</ul>
</div>
</section>
</div>
<script src="app.js"></script>
</body>
</html>
I found this article that's supposed to be related to what I was looking for, which is sorting a list by class. However, in my code, it didn't work. So I'm trying to figure out how to solve the problem. I have two classes, "offline" and "none". I want the class "offline" to come at top and the class "none" to appear at bottom under "offline" area. I have one more class in each div's which is "indbox", therefore, I tried to use "getElementsByClassName" but it's not working.
Here's my code from codepen.
$(document).ready(function() {
$(".con-button").click(function(){
var cssObj = {};
cssObj.left = $(this).position().left;
cssObj.width = $(this).outerWidth();
$(".controls .effect").css( cssObj );
if(this.id == "c-all") {
$('.offline').hide();
$('.offline').fadeIn("slow").show();
$('.online').hide();
$('.online').fadeIn("slow").show();
$('.none').fadeIn("slow").show();
} else if (this.id == "c-online") {
$('.offline').hide();
$('.online').hide();
$('.online').fadeIn("slow").show();
$('.none').hide();
} else if (this.id == "c-offline") {
$('.offline').hide();
$('.offline').fadeIn("slow").show();
$('.online').hide();
$('.none').hide();
}
});
$(".con-button").eq(0).trigger("click");
getSteams();
var elem = $('#offline').find('div').sort(sortMe);
function sortMe(a, b) {
return a.getElementsByClassName("offline") < b.getElementsByClassName("none");
}
$('#offline').append(elem);
});
var channels = ["BasicallyIDoWrk", "FreeCodeCamp", "Golgothus", "OgamingSC2", "maatukka", "Vinesauce", "brunofin", "comster404", "esl_csgo"];
var cb = "?client_id=egn4k1eja0yterrcuu411n5e329rd3&callback=?";
function getSteams() {
channels.forEach(function(indchannel) {
//for (var channel in channels) {
//var indchannel = channel;
var streamURL = "https://api.twitch.tv/kraken/streams/" + indchannel + cb;
var channelURL = "https://api.twitch.tv/kraken/channels/" + indchannel + cb;
$.ajax({
url: streamURL,
type: 'GET',
dataType: "jsonp",
data: {
//action: 'query',
format: 'json',
},
headers: {
"Accept": "application/vnd.twitchtv.v5+json",
},
success: function(data) {
var game;
var status;
if(data.stream === null) {
$.getJSON(data._links.channel + "/?client_id=egn4k1eja0yterrcuu411n5e329rd3&callback=?", function(data2) {
if(data2.status == 404) {
game = "The Account doesn't exist";
status = "none";
} else {
game = "Offline";
status = "offline";
}
$("#offline").append('<div class="indbox ' + status + '"><a target="_blank" href="#">'+ indchannel + '<br/>' + game +'</a></div>');
} );
} else {
game = data.stream.game;
status = "online";
$("#online").append('<div class="indbox ' + status + '"><a target="_blank" href="#">'+ indchannel + '<br/>' + game +'</a></div>');
};
}
});
});
}
html, body{
height:100%;
margin: 0;
background-color: #ffffff;
}
.wrapper {
text-align: center;
position: relative;
width: 100%;
height: 100%;
display:block;
}
.container {
width: 75%;
margin: 30px auto 0;
position: relative;
}
.logobox img {
width: 20%;
margin: 0 auto;
}
.controls {
position: relative;
width: 100%;
}
.con-button {
position: relative;
background-color: white;
border: none;
margin: 0.5em 0 0 0;
padding: 0.5em 1em 0.5em 1em;
text-align: center;
color: rgb(100,65,164);
font-size: 20px;
transition: .4s;
}
.con-button:hover {
cursor: pointer;
/*border-bottom: 3px solid rgba(224, 217, 236, 1);*/
}
.con-button:focus {outline: 0;}
.divider hr {
border-top: 1px solid #6441A4;
}
.effect {
position: absolute;
display: block;
left: 0;
bottom: 5px;
height: 2px;
width: 60px;
transition: 0.4s ease-in-out;
/*border-bottom: 3px solid rgba(100, 65, 164, 1);*/
background: #6441A4;
}
.indbox {
width: 100%;
display: block;
margin: 5px 0px;
padding: 8px 0px;
}
.online {
background-color: #98FB98;
}
.offline {
background-color: #ff9999;
}
.none {
background-color: #D3D3D3;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container">
<div class="twitchtvarea">
<div class="logobox">
<img src="https://s6.postimg.org/bay7stotd/Twitch_Purple_RGB.png" />
</div>
<div class="twitchbox">
<div class="controls">
<button id="c-all" class="con-button active" type="button">All</button>
<button id="c-online" class="con-button" type="button">Online</button>
<button id="c-offline" class="con-button" type="button">Offline</button>
<div class="effect"></div>
</div>
<div class="divider"><hr></div>
<div id="online"></div>
<div id="offline"></div>
</div>
</div>
</div>
</div>
The code I would like for you to focus on is:
var elem = $('#offline').find('div').sort(sortMe);
function sortMe(a, b) {
return a.getElementsByClassName("offline") < b.getElementsByClassName("none");
}
$('#offline').append(elem);
Please help me fix it.
Looking through your code, I find out that you are using thisSort Function; however, your way of doing is incorrect. In the example, they have:
function compare(a, b) {
if (a is less than b by some ordering criterion) {
return -1;
}
if (a is greater than b by the ordering criterion) {
return 1;
}
// a must be equal to b
return 0;
}
So in order to sort "offline" before "none", your function has to return 1
function sortMe(a, b){
if (a.hasClass("offline")) {
return 1; //if an element has offline, move it above.
} else {
return -1; //if an element doesn't have offline, it means it's none. Move it down.
}
}
You might want to add in the condition to check whether they both have offline class.
Your problem can be solved by using the :
appendTo();
method.
instead of the :
append();
method.
I added two additional divs in your html code and made a small change to your javascript code ant it works !!!
the html goes like this :
<div class="divider"><hr></div>
<div id="online"></div>
<div id="offline">
<div class="notconnected"></div>
<div class="nonexisting"></div>
</div>
</div>
and the javascript was changed here :
if(data2.status == 404) {
game = "The Account doesn't exist";
status = "none";
}
else {
game = "Offline";
status = "offline";
}
if(status==="none"){
$('<div class="indbox ' + status + '" id="'+status+'"><a target="_blank" href="#">'+ indchannel + '<br/>' + game +'</a></div>').appendTo(".nonexisting");}
else{
$('<div class="indbox ' + status + '" id="'+status+'"><a target="_blank" href="#">'+ indchannel + '<br/>' + game +'</a></div>').appendTo(".notconnected");
}
} );
The Documentation for the method is here : appendTo()