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>
I've added a back button to my calculator with the idea that when someone presses it they can undo if they clicked the wrong number.
I've tried to solve using substr within an if statement in my calculate function but nothing seems to be happening it's as if it's not recording when the user clicks the button. To be honest I'm not sure this is the correct approach!
//Create variable to hold display value, create variable of display area and populate.
let toShow = 0;
let displayValue = 0;
let displayArea = document.getElementById('result-display');
let dataType = document.querySelectorAll('[data-type]');
let operator = "";
let firstNumber = 0;
let secondNumber = 0;
let operatorPressed = false;
let secondNumberCounter = false;
let firstNumberCounter = false;
let buttonNumber = 0;
let decimalCounter = 0;
displayArea.innerHTML = 0;
/* displayArea.innerHTML += displayValue; */
//Basic math functions
add = (firstNumber, secondNumber) => firstNumber + secondNumber;
subtract = (firstNumber, secondNumber) => firstNumber - secondNumber;
multiply = (firstNumber, secondNumber) => firstNumber * secondNumber;
divide = (firstNumber, secondNumber) => firstNumber / secondNumber;
//Take an operator and two numbers and call a math function
operate = (operator, firstNumber, secondNumber) => {
toShow = operator(firstNumber, secondNumber);
return Math.round(toShow * 100) / 100;
};
//Update display with button clicked
const button = document.querySelectorAll('.item');
addEventListener('click', e => {
calculate(e);
});
let calculate = (e) => {
if (e.target.id === "equals" && secondNumber === 0) {
displayArea.innerHTML = "OH HELL NO!";
} else if (e.target.id === "equals") {
decimalCounter = 0;
switch (operator) {
case "add":
secondNumberCounter = false;
return firstNumber = displayArea.innerHTML = operate(add, firstNumber, secondNumber);
case "multiply":
secondNumberCounter = false;
return firstNumber = displayArea.innerHTML = operate(multiply, firstNumber, secondNumber);
case "divide":
secondNumberCounter = false;
return firstNumber = displayArea.innerHTML = operate(divide, firstNumber, secondNumber);
case "subtract":
secondNumberCounter = false;
return firstNumber = displayArea.innerHTML = operate(subtract, firstNumber, secondNumber);
}
}
if (operatorPressed === true && e.target.dataset.type === "number") {
if (secondNumberCounter === false) {
displayArea.innerHTML = null;
}
if (decimalCounter === 0) {
let secondButtonNumber = e.target.innerText;
displayValue = secondButtonNumber;
displayArea.innerHTML += displayValue;
secondNumber = Number(displayArea.innerHTML);
secondNumberCounter = true;
}
} else if (e.target.dataset.type === "nonNumber") {
decimalCounter = 0;
operator = e.target.id;
firstNumber = Number(displayArea.innerText);
operatorPressed = true;
} else {
if (e.target.id === "decimal" && decimalCounter === 0) {
++decimalCounter;
buttonNumber = e.target.innerText;
displayValue = buttonNumber;
displayArea.innerHTML += displayValue;
console.log(buttonNumber);
} else {
buttonNumber = Number(e.target.innerText);
displayValue = buttonNumber;
displayArea.innerHTML += displayValue;
displayArea.innerHTML = Number(displayArea.innerHTML);
}
}
if (e.target.id === "AC") {
secondNumberCounter = false;
operatorPressed = false;
firstNumber = 0;
secondNumber = 0;
buttonNumber = 0;
decimalCounter = 0;
displayArea.innerHTML = 0;
}
}
body {
display: flex;
align-items: center;
justify-content: center;
border: 1px solid black;
flex-direction: row;
width: 100%;
height: 100vh;
}
.container {
display: flex;
align-items: center;
justify-content: center;
background-color: gray;
width: 275px;
height: 450px;
border: 1px solid black;
}
.sub-container {
display: flex;
flex-direction: column;
align-items: center;
height: 95%;
width: 800%;
}
.display {
display: flex;
align-items: center;
justify-content: flex-end;
border: 1px solid white;
width: 80%;
height: 10%;
margin: 5% 0 5% 0;
color: white;
}
#result-display {
margin-right: 8%;
}
.button-grid {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
height: 100%;
width: 80%;
}
.item {
width: 50px;
height: 50px;
border: 1px solid white;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
color: black;
}
button {
width: 100%;
height: 100%;
}
footer {
width: 100%;
background-color: gray;
}
Make the calculator work! You’ll need to store the first number that is input into the calculator when a user presses an operator,
and also save which operation has been chosen and then operate() on them when the user presses the “=” key.
<div class="container">
<div class="sub-container">
<div class="display">
<p id="result-display">
</p>
</div>
<div class="button-grid">
<div>
<button class="item" id="7" data-type="number">
7
</button>
</div>
<div>
<button class="item" id="8" data-type="number">
8
</button>
</div>
<div>
<button class="item" id="9" data-type="number">
9
</button>
</div>
<div>
<button class="item" id="divide" data-type="nonNumber">
*/*
</button>
</div>
<div>
<button class="item" id="4" data-type="number">
4
</button>
</div>
<div>
<button class="item" id="5" data-type="number">
5
</button>
</div>
<div>
<button class="item" id="6" data-type="number">
6
</button>
</div>
<div>
<button class="item" id="multiply" data-type="nonNumber">
X
</button>
</div>
<div>
<button class="item" id="1" data-type="number">
1
</button>
</div>
<div>
<button class="item" id="2" data-type="number">
2
</button>
</div>
<div>
<button class="item" id="3" data-type="number">
3
</button>
</div>
<div>
<button class="item" id="subtract" data-type="nonNumber">
-
</button>
</div>
<div>
<button class="item" id="0" data-type="number">
0
</button>
</div>
<div>
<button class="item" id="decimal" data-type="number">
.
</button>
</div>
<div>
<button class="item" id="AC" data-type="nonNumber">
AC
</button>
</div>
<div>
<button class="item" id="add" data-type="nonNumber">
+
</button>
</div>
<div>
<button class="item" id="equals" data-type="nonNumber">
=
</button>
</div>
<div>
<button class="item" id="back" data-type="nonNumber">
<
</button>
</div>
</div>
</div>
</div>
<div>
<!-- User presses back button
preprend displayValue
displayValue remove one
displayArea.innerHTML remove one
let str = 'Mozilla';
let smallStr = str.substr(0, str.length - 1);-->
You can use either The Memento Pattern or The Command Pattern explained here:
How to make a undo/redo function
I ended up solving this using substring. Basically I look for the length of the string in my calculators display then remove the last character.
Code used in an IF Statement:
if (e.target.id === "back") {
displayArea.innerHTML = displayArea.innerHTML.substring(0, displayArea.innerHTML.length - 1);
}
I'm working on a website which uses fullpage.js and has a section with several slides. The slides switch automatically every 10 seconds.
I wanted to create those bars like Instagram has them, that slowly fill up. When one bar is filled, the viewport changes to the next page and the next bar starts to fill itself. And when the user manually switches slides the according bar should start animating.
I didn't find anything online so I built it from scratch and after some attempts I got it working as I wanted. BUT the code ist horrible.
I am 100% sure it is possible to bring it down to a few lines but as I'm not skilled enough to refactor it that way. How could this function be solved in a better way, with less code? Any help would help me a lot to learn here.
Here's the relevant code I wrote (It won't work properly, because it's based on the URL, but I'm sure you'll get how it would work with the correct URL)
function locationHashChanged() {
if (location.hash === '#page2') {
document.getElementById("Loadingbar-2-1").setAttribute("style","animation-name:loadingbar;animation-duration:10s;animation-timing-function:linear;");
document.getElementById("Loadingbar-2-2").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-3").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-4").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-5").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-6").setAttribute("style","width:0%");
} else if (location.hash === '#page2/1') {
document.getElementById("Loadingbar-2-1").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-2").setAttribute("style","animation-name:loadingbar;animation-duration:10s;animation-timing-function:linear;");
document.getElementById("Loadingbar-2-3").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-4").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-5").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-6").setAttribute("style","width:0%");
} else if (location.hash === '#page2/2') {
document.getElementById("Loadingbar-2-1").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-2").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-3").setAttribute("style","animation-name:loadingbar;animation-duration:10s;animation-timing-function:linear;");
document.getElementById("Loadingbar-2-4").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-5").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-6").setAttribute("style","width:0%");
} else if (location.hash === '#page2/3') {
document.getElementById("Loadingbar-2-1").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-2").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-3").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-4").setAttribute("style","animation-name:loadingbar;animation-duration:10s;animation-timing-function:linear;");
document.getElementById("Loadingbar-2-5").setAttribute("style","width:0%");
document.getElementById("Loadingbar-2-6").setAttribute("style","width:0%");
} else if (location.hash === '#page2/4') {
document.getElementById("Loadingbar-2-1").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-2").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-3").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-4").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-5").setAttribute("style","animation-name:loadingbar;animation-duration:10s;animation-timing-function:linear;");
document.getElementById("Loadingbar-2-6").setAttribute("style","width:0%");
} else if (location.hash === '#page2/5') {
document.getElementById("Loadingbar-2-1").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-2").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-3").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-4").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-5").setAttribute("style","width:100%");
document.getElementById("Loadingbar-2-6").setAttribute("style","animation-name:loadingbar;animation-duration:10s;animation-timing-function:linear;");
}
}
window.onload = locationHashChanged;
window.onhashchange = locationHashChanged;
.loading-bars-container {
position: absolute;
left: 48px;
right: 48px;
bottom: 0.5em;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
}
.storyindicator {
position: relative;
z-index: 1000;
overflow: hidden;
width: 20%;
height: 4px;
margin-right: 0.25em;
margin-left: 0.25em;
border-radius: 4px;
background-color: #000;
}
.loadingbar-fill {
position: absolute;
left: 0;
top: 0;
bottom: 0;
background-color: grey;
}
<div class="loading-bars-container">
<div class="storyindicator">
<div id="Loadingbar-2-1" class="loadingbar-fill" style="width:100%"></div>
</div>
<div class="storyindicator">
<div id="Loadingbar-2-2" class="loadingbar-fill" style="width:100%"></div> </div>
<div class="storyindicator">
<div id="Loadingbar-2-3" class="loadingbar-fill" style="width:100%"></div> </div>
<div class="storyindicator">
<div id="Loadingbar-2-4" class="loadingbar-fill" style="animation-name:loadingbar;animation-duration:10s;animation-timing-function:linear;"></div> </div>
<div class="storyindicator">
<div id="Loadingbar-2-5" class="loadingbar-fill" style="width:0%"></div> </div>
<div class="storyindicator">
<div id="Loadingbar-2-6" class="loadingbar-fill" style="width:0%"></div> </div>
</div>
If you want the code to be shorter (sometimes it's totally OK for code to be verbose), I would propose this:
const animationStr = "animation-name:loadingbar;animation-duration:10s;animation-timing-function:linear;";
const barsCnt = 6;
function locationHashChanged() {
const num = location.hash.match(/^#page2(?:\/(\d))?$/)[1] || 0;
for (let i = 0; i < barsCnt; i++) {
const style = i < num
? "width:100%"
: i > num
? "width:0%"
: animationStr;
document.getElementById("Loadingbar-2-" + (i + 1)).setAttribute("style", style);
}
}
window.onload = locationHashChanged;
window.onhashchange = locationHashChanged;
I tested it, but there still may be some flaws, please check it.
I coded a game for 2 people:
Initialization is done with player1 ()
with the displayScore () function I can reiterate the score as much as I want (here everything is fine)
but when I switch to the other player with the changePlayer (player) function, the event goes well with player2 I can also reiterate but the concern is that the first player continues to play while my functions and variables are well partitioned
the problem comes from the event of the first which continues despite my stopPropagation () which I did not forget to report.
Where's the problem I've been on for a day and a half.
I join the two files html and js and also css
Thank you in advance,
cordially.
jeu de dés
<body>
<div id="launchGame">
<h1>NEW GAME</h1>
</div>
<div id="players">
<div id="player_1"><h2>PLAYER 1</h2><span id="score_player_1">Score :</span></div>
<picture><img id="dice">img</picture>
<div id="player_2"><h2>PLAYER 2</h2><span id="score_player_2">Score :</span></div>
</div>
<div id="current_score">
<div id="current_score_1"><p>current<span id="round_player_1"></span></p></div>
<div id="controls_game">
<h3 id="roll_dice"><img src="images/re_game.png">ROLL DICE</h3>
<h3 id="hold"><img src="images/charge_score.png">HOLD</h3>
</div>
<div id="current_score_2"><p>current<span id="round_player_2"></span></p></div>
</div>
</body>
(function(){
window.addEventListener("DOMContentLoaded", function() {
const launch = document.getElementById("launchGame");
const rollDice = document.getElementById("roll_dice");
function currentPlayer(player) {
const hold = document.getElementById("hold");
if (player.getElementsByTagName("img").length < 1) {
player.firstChild.style.position = "relative";
var currentPlayer = new Image();
currentPlayer.src = "images/current_player.png";
currentPlayer.setAttribute("class", player.getAttribute("id"));
currentPlayer.style.position = "absolute";
player.firstChild.appendChild(currentPlayer);
} else {
player.getElementsByTagName("img")[0].style.visibility = "visible";
}
changePlayer(player);
rollDice.addEventListener("click", function(e) {
e.stopPropagation();
displayScore(player);
}, {capture:false, once: false, useCapture: true});
}
function player1() {
let player1 = document.getElementById("player_1");
currentPlayer(player1);
}
function player2() {
let player2 = document.getElementById("player_2");
currentPlayer(player2);
}
function changePlayer(player) {
hold.addEventListener("click", function(e) {
e.stopPropagation();
player.getElementsByTagName("img")[0].style.visibility = "hidden";
if (player.getAttribute("id") === "player_1") {
console.log(player.getAttribute("id"));
player2();
} else if (player.getAttribute("id") === "player_2") {
console.log(player.getAttribute("id"));
player1();
}
}, {capture:false, once:true, useCapture: true});
}
function displayScore(player) {
let scoreDice = getScoreDice();
let scorePlayer = document.getElementById("round_" +
player.getAttribute("id"));
scorePlayer.textContent = scoreDice();
}
function getScoreDice() {
var result = 0;
var faceDice = Math.floor(Math.random()*7);
if ((faceDice > 0) && (faceDice < 7)) {
result = faceDice;
} else {
result = faceDice+1;
}
function innerGetScoreDice() {
return result;
}
return innerGetScoreDice;
}
launch.addEventListener("click", player1(), {capture:false, once:true});
});
})()
body {
margin: 0;
padding: 0;
background-color: silver;
background: linear-gradient(to left, white, white 50%, rgb(228, 227, 227) 50%, rgb(228, 227, 227));
text-align: center;
color: gray;
}
h1 {
width: 40%;
margin: 1% auto 5%;
font-size: 1.5rem;
}
h1::before {
content: "⊕ ";
font-size: 2rem;
align-items: center;
color: tomato;
}
h2 {
height: 30px;
line-height: 30px;
}
h3 {
margin: 7% 0;
}
span {
display: block;
margin-top: 50%;
}
img {
max-width: 10%;
vertical-align: middle;
margin-right: 3%;
}
#players, #final_score, #current_score, #current_score_1, #current_score_2, #controls_game {
display: flex;
}
#players, #final_score, #current_score {
flex-direction: row;
justify-content:space-evenly;
align-items: center;
}
#players {
justify-content:space-around;
margin: 1% 1% 10%;
}
#current_score_1, #current_score_2 {
border-radius: 20%;
padding: 2% 4%;
background-color: tomato;
color: white;
}
#controls_game {
flex-direction: column;
max-width: 33%;
}
In function changePlayer(player), isn't variable 'hold' undefined ?
No
The variable hold is defined and ok
All variables are ok
Problems is in event of first players
Bubble or capture
I don't now whats this
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.