Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 236 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
/*----- constants -----*/

// 1) Define required constants

const PLAYERS = {
0: "init",
1: "red",
"-1": "green",
};

/*----- state variables -----*/
// 2) Define required variables used to track the state of the game

let board; // array of 7 column arrays
let turn = 0; // 1 or -1
let winner = 0; // null = no winner; 1 or -1 = winner; 'T' = Tie
let redWins = 0;
let greenWins = 0;

/*----- cached elements -----*/
//DOM ELEMENTS
const message = document.getElementById("message");
const playerTurnDisplay = document.getElementById("player-id");
const boardCells = document.querySelectorAll(".cells");
const resetButton = document.getElementById("reset-button");
const redCounter = document.getElementById("redWins");
const greenCounter = document.getElementById("greenWins");
/*----- event listeners -----*/
resetButton.addEventListener("click", function () {
init();
message.style.display = "none";
});
/*----- functions -----*/
init();

// 3) Store elements on the page that will be accessed in code more than once in variables to make code more concise, readable and performant.

// 4) Upon loading the app should:
// 4.1) Initialize the state variables
// 4.2) Render those values to the page
// 4.3) Wait for the user to click a square
function init() {
//turn 90deg counterclock to visualize
board = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
];
turn = 1;
winner = null;
playerTurnDisplay.innerHTML = "GREEN'S";
playerTurnDisplay.style.color = "green";

// Clear the border colors of the cells
boardCells.forEach((cell) => {
cell.style.borderColor = "";
});
}
//handles click and changes colors
function handleClick(e) {
boardCells.forEach((cell) => {
cell.addEventListener("click", (evt) => {
if (winner !== null) {
// If there's a winner, return early and do not allow further moves
return;
}

if (!evt.target.style.borderColor) {
const row = parseInt(evt.target.id.charAt(3));
const col = parseInt(evt.target.id.charAt(1));

turn = turn * -1;
if (turn === -1) {
playerTurnDisplay.style.color = "red";
playerTurnDisplay.innerHTML = "RED'S";
} else if (turn === 1) {
playerTurnDisplay.style.color = "green";
playerTurnDisplay.innerHTML = "GREEN'S";
}
board[row][col] = turn;
console.log(evt.target.id);
let clickedSquare = evt.target.id;
console.log(clickedSquare);
evt.target.style.borderColor = turn === 1 ? "red" : "green";
console.log(board);
checkRowWinner();
checkColWinner();
checkDiaganolWinner();
checkTie();
isThereAWinner();
}

// const squareId = cell.id;
// console.log(`Square ${squareId} was clicked.`);
});
});
}
handleClick();

function checkRowWinner() {
for (let i = 0; i < board.length; i++) {
let total = 0;

board[i].forEach((cell) => {
total += cell;
});

if (total === 3) {
console.log("RED WINS");
return (winner = 1);
console.log(winner);
} else if (total === -3) {
console.log("GREEN WINS");
return (winner = -1);
}
}
}

function checkColWinner() {
for (let i = 0; i < board.length; i++) {
let total = board[0][i] + board[1][i] + board[2][i];
if (total === 3) {
console.log("RED WINS");
return (winner = 1);
} else if (total === -3) {
console.log("GREEN WINS");
return (winner = -1);
}
}
}

function checkDiaganolWinner() {
if (
board[0][0] + board[1][1] + board[2][2] === 3 ||
board[0][2] + board[1][1] + board[2][0] === 3
) {
console.log("RED WINS");
return (winner = 1);
} else if (
board[0][0] + board[1][1] + board[2][2] === -3 ||
board[0][2] + board[1][1] + board[2][0] === -3
) {
console.log("GREEN WINS");
return (winner = -1);
}
}
function checkTie() {
if (winner === null) {
let isTie = true;

board.forEach((cellArr) => {
cellArr.forEach((cell) => {
if (cell === 0) {
isTie = false;
}
});
});

if (isTie) {
winner = "T";
}
}
}

function isThereAWinner() {
if (winner === 1) {
message.innerHTML = "RED WINS";
message.style.fontSize = "15px";
message.style.color = "red";
} else if (winner === -1) {
message.innerHTML = "GREEN WINS";
message.style.fontSize = "15px";
message.style.color = "green";
} else if (winner === "T") {
message.innerHTML = "ITS A TIE!";
message.style.fontSize = "15px";
message.style.color = "blue";
}
}

// 5) Handle a player clicking a square:
// 5.1) Obtain the index of the square that was clicked by either:
// 5.1.1) "Extracting" the index from an id assigned to the element in the HTML, or
// 5.1.2) Looping through the cached square elements using a for loop and breaking out when the current square element equals the event object's target.

// 5.2) If the board has a value at the index, immediately return because that square is already taken.
// 5.3) If winner is not null, immediately return because the game is over.
// 5.4) Update the board array at the index with the value of turn.
// 5.5) Flip turns by multiplying turn by -1 (flips a 1 to -1, and vice-versa).
// 5.6) Set the winner variable if there's a winner:
// 5.6.1) Loop through the each of the winning combination arrays defined.
// 5.6.2) Total up the three board positions using the three indexes in the current combo.
// 5.6.3) Convert the total to an absolute value (convert any negative total to positive).
// 5.6.4) If the total equals 3, we have a winner! Set winner to the board's value at the index specified by the first index in the combo array. Exit the loop.
// 5.7) If there's no winner, check if there's a tie:
// 5.7.1) Set winner to 'T' if there are no more nulls in the board array.
// 5.8) All state has been updated, so render the state to the page (step 4.2).

// 6) Handle a player clicking the replay button
// Since most web apps are event-driven by nature. Coding an event-driven program generally requires that we set up the application when it loads (steps 1 - 4) and wait for the user to interact with the app (steps 5 & 6).

// Now we can start adding as many detailed steps as desired.

// Typically, pseudocode does not have to be as detailed as the following - it is highly detailed here to help you as much as possible:

// 1) Define required constants:
// 1.1) Define a colors object with keys of 'null' (when the square is empty), and players 1 & -1. The value assigned to each key represents the color to display for an empty square (null), player 1 and player -1.
// 1.2) Define the 8 possible winning combinations, each containing three indexes of the board that make a winner if they hold the same player value.

// 2) Define required variables used to track the state of the game:
// 2.1) Use a board array to represent the squares.
// 2.2) Use a turn variable to remember whose turn it is.
// 2.3) Use a winner variable to represent three different possibilities - player that won, a tie, or game in play.

// 3) Store elements on the page that will be accessed in code more than once in variables to make code more concise, readable and performant:
// 3.1) Store the 9 elements that represent the squares on the page.

// 4) Upon loading the app should:
// 4.1) Initialize the state variables:
// 4.1.1) Initialize the board array to 9 nulls to represent empty squares. The 9 elements will "map" to each square, where index 0 maps to the top-left square and index 8 maps to the bottom-right square.
// 4.1.2) Initialize whose turn it is to 1 (player 'X'). Player 'O' will be represented by -1.
// 4.1.3) Initialize winner to null to represent that there is no winner or tie yet. Winner will hold the player value (1 or -1) if there's a winner. Winner will hold a 'T' if there's a tie.
// 4.2) Render those state variables to the page:
// 4.2.1) Render the board:
// 4.2.1.1) Loop over each of the 9 elements that represent the squares on the page, and for each iteration:
// 4.2.1.1.2) Use the index of the iteration to access the mapped value from the board array.
// 4.3.1.1.3) Set the background color of the current element by using the value as a key on the colors lookup object (constant).
// 4.2.2) Render a message:
// 4.2.2.1) If winner has a value other than null (game still in progress), render whose turn it is - use the color name for the player, converting it to upper case.
// 4.2.2.2) If winner is equal to 'T' (tie), render a tie message.
// 4.2.2.3) Otherwise, render a congratulatory message to which player has won - use the color name for the player, converting it to uppercase.
// 4.3) Wait for the user to click a square

// 6) Handle a player clicking the replay button:
// 6.1) Do steps 4.1 (initialize the state variables) and 4.2 (render).
// Using a numbered outline is not required but helps organize the more complex steps into detailed steps.
43 changes: 43 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Tic Tac Toe</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<script defer src="app.js"></script>
</head>

<body>
<h1><span id="player-id">PURPLE</span>'S TURN</h1>
<div id="main-game-container">

<section id="board">
<div class="cells" id="c0r0"></div>
<div class="cells" id="c1r0"></div>
<div class="cells" id="c2r0"></div>
<div class="cells" id="c0r1"></div>
<div class="cells" id="c1r1"></div>
<div class="cells" id="c2r1"></div>
<div class="cells" id="c0r2"></div>
<div class="cells" id="c1r2"></div>
<div class="cells" id="c2r2"></div>

</section>

</div>


<div id="btn-container"><button>PLAY AGAIN</button></div>

<body>
</body>

</html>
</body>

</html>
55 changes: 55 additions & 0 deletions styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#main-game-container {
display: flex;
align-items: center;
justify-content: center;
background-color: ;
}

#board {
display: inline-grid;
grid-template-columns: repeat(3, 1fr);
column-gap: 2px;
row-gap: 2px;
}
#board > div {
text-align: center;
border: 80px solid black;
opacity: 0.2;
}

#board > div:hover {
transform: scale(1.1);
}

#btn-container {
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
}

button {
color: white;
font-size: 2rem;
border-radius: 10%;
background-color: black;
opacity: 0.2;
}
button:hover {
color: white;
font-size: 2rem;
border-radius: 10%;
background-color: black;
opacity: 0.9;
}

h2 {
text-align: center;
}

h1 {
text-align: center;
}
#player-id {
color: green;
}