Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finished TTT deliverable #267

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
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
35 changes: 35 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!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="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300&display=swap" rel="stylesheet">
<script defer src="main.js"></script>
<link rel="stylesheet" href="style.css">
<title>Tic Tac Toe</title>
</head>
<body>
<header>TIC TAC TOE</header>
<h1>PLACEHOLDER: X's Turn</h1>

<!-- Connect the board to the DOM -->
<section id="board">
<!-- row 2 -->
<div id="c0r2"></div>
<div id="c1r2"></div>
<div id="c2r2"></div>
<!-- row 1 -->
<div id="c0r1"></div>
<div id="c1r1"></div>
<div id="c2r1"></div>
<!-- row 0 -->
<div id="c0r0"></div>
<div id="c1r0"></div>
<div id="c2r0"></div>
</section>
<button>PLAY AGAIN</button>
</body>
</html>
183 changes: 183 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*----- constants -----*/
const MOVES = {
// UPDATE - NOT A COLOR LOOK UP, BUT LETTER(X OR O)
'0' : ' ',
'1' : 'X',
'-1': 'O'
};


/*----- state variables -----*/

// The board: 2D array with 3 col and 3 rows, initialized in init function
let board;
// The current player/turn (1 or -1)
let turn;
// Winner: null while in play; then 1/-1; for tie: "T"
let winner;


/*----- cached elements -----*/
const messageEl = document.querySelector('h1');
const playAgainBtn = document.querySelector('button');
// const squareEls = [...document.querySelectorAll('#board > div')];

/*----- event listeners -----*/
// Listen for a click in the board
document.getElementById('board').addEventListener('click', handleFillSquare);
// Start over by calling init when clicked; DO NOT INVOKE
playAgainBtn.addEventListener('click', init);

/*----- functions -----*/
function init(){
//initializing the let board var; rotate 90 deg counterclockwise to visualize the board in the DOM
board = [
[" ", " ", " "], // col 0
[" ", " ", " "], // col 1
[" ", " ", " "], // col 2
];
//game begins with Player 1
turn = 1;
winner = null;
render();
}

function render(){
// Display updated border after player makes a move
renderBoard();
// Display a message if there's a winner
renderMessage();
renderControls();
}

function renderBoard(){
//Iterate through the board's array to determine what displays on the board
board.forEach(function(colArr){
let colIdx = board.indexOf(colArr)
// Get the index of each elem; can't use indexOf bc elems are not unique
let rowIdx = -1;
colArr.forEach(function(rowArr){
rowIdx += 1;
cellId = `c${colIdx}r${rowIdx}`
const cellEl = document.getElementById(cellId);
cellEl.style.textAlign = "center";
cellEl.style.lineHeight = "10vmin";
cellEl.innerText = board[colIdx][rowIdx];

})
})
}

function renderMessage(){
if (winner === 'T') {
messageEl.innerText = "It's a tie!!!"
}
// Declare winner if truthy, but not T (tie)
else if (winner) {
messageEl.innerHTML = `${winner} won!`;
}
// Someone's turn
else {
messageEl.innerHTML = `It's ${MOVES[turn].toUpperCase()}'s Turn`;

}
}

function renderControls(){
// Hide Play Again if Winner isn't truthy (tie or winner)
playAgainBtn.style.visibility = winner ? 'visible' : 'hidden';
//Iterate over the marker elems to hide triangles for full cols
}

//In response to user interaction, update all impacted state then call render()
function handleFillSquare(e){
const clickedSq = e.target.id;
colIdx = clickedSq.substr(1,1)
rowIdx = clickedSq.substr(3,3)
if (board[colIdx][rowIdx] === " ") {
board[colIdx][rowIdx] = MOVES[turn];
turn *= -1;
winner = getWinner(colIdx, rowIdx)
render();
} else {
messageEl.innerText = "That spot is taken! Try again."
}
};

// Check for the winner in board state and return null if no winner
// Return 1/-1 if a player won, or T if tie
function getWinner(colIdx, rowIdx) {
// Pass in the last play to the check functions
return checkVerticalWin(colIdx, rowIdx) ||
checkHorizontalWin(colIdx, rowIdx) ||
checkDiagonalWinNESW(colIdx, rowIdx) ||
checkDiagonalWinNWSE(colIdx, rowIdx) ||
// Check for full board if there isn't a winner yet
checkTie();
}

function checkTie () {
let filledSpaces = 0;
// Nexted forEach not working; using for loop
for (col in board){
for (row in board[col]){
if (board[col][row] !== " "){
filledSpaces += 1;
}
}
}
console.log(filledSpaces)
return filledSpaces === 9 ? "T" : null;
}

function checkDiagonalWinNESW (colIdx,rowIdx){
let lastMove = board[colIdx][rowIdx]
let matchCount = 0;
let neswIdx = 0;
board.forEach(function(col){
if (col[neswIdx] === lastMove){
matchCount++;
neswIdx++;
}
})
return matchCount === 9 ? lastMove : null;
}

function checkDiagonalWinNWSE (colIdx,rowIdx){
let lastMove = board[colIdx][rowIdx]
let matchCount = 0;
let nwseIdx = 2;
board.forEach(function(col){
if (col[nwseIdx] === lastMove){
matchCount++;
nwseIdx -= 1;
}
})
return matchCount === 3 ? lastMove : null;
}

// Check for horizontal win
function checkHorizontalWin (colIdx, rowIdx) {
let lastMove = board[colIdx][rowIdx]
let matchCount = 0;
board.forEach(function(col){
if (col[rowIdx] === lastMove){
matchCount++;
}
})
return matchCount === 3 ? lastMove : null;
}

// Check for vertical win (count the # of same elem in the col below last play)
function checkVerticalWin(colIdx, rowIdx){
let lastMove = board[colIdx][rowIdx]
let matchCount = 0;
board[colIdx].forEach(function(rowEl){
if (rowEl === lastMove){
matchCount++;
}
})
return matchCount === 3 ? lastMove : null;
}

init();
76 changes: 76 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
* {
box-sizing: border-box;
}

body {
/* viewport units - vh (viewport height), vw (v width), vmin */
height: 100vh;
margin: 0;
/* Setting body font applies to most things except buttons */
font-family: 'Roboto Condensed', sans-serif;
/* Turn the body into a flexbox */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}

header {
/* Sets font to 4% of whatever is on the page */
font-size: 4vmin;
color: darkmagenta;
letter-spacing: 1vmin;
}

h1 {
color: hotpink;
font-size: 3vmin;
}


#board {
display: grid;
/* set the width of the 3 columns */
grid-template-columns: repeat(3, 10vmin);
/* set the width of the 3 rows */
grid-template-rows: repeat(3, 10vmin);
/* add gap between columns */
gap: 0vmin;
}
#board > div#c1r1 {
border: 0.3vmin solid darkmagenta;
}
#board > div#c1r2 {
border-left: 0.3vmin solid darkmagenta;
border-right: 0.3vmin solid darkmagenta;

}
#board > div#c0r1 {
border-top: 0.3vmin solid darkmagenta;
border-bottom: 0.3vmin solid darkmagenta;

}
#board > div#c2r1 {
border-top: 0.3vmin solid darkmagenta;
border-bottom: 0.3vmin solid darkmagenta;
}
#board > div#c1r0 {
border-left: 0.3vmin solid darkmagenta;
border-right: 0.3vmin solid darkmagenta;
}


button {
margin: 4vmin;
padding: 2vmin;
font-size: 2vmin;
border-radius: 4vmin;
border: 0.1vmin solid darkmagenta;
background-color: hotpink;
color: white;
}

button:hover {
color: white;
background-color: darkmagenta;
}