From b3d97b60582dcc61641ef6027792420f06184b60 Mon Sep 17 00:00:00 2001 From: lvlivingston Date: Fri, 29 Sep 2023 19:28:22 -0500 Subject: [PATCH 1/8] setup file structure --- css/main.css | 0 index.html | 0 js/main.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 css/main.css create mode 100644 index.html create mode 100644 js/main.js diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..e69de29 diff --git a/index.html b/index.html new file mode 100644 index 0000000..e69de29 diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..e69de29 From 6ddf4dbe3a533a2c28cecfdee80294cab9ace212 Mon Sep 17 00:00:00 2001 From: lvlivingston Date: Sat, 30 Sep 2023 15:47:43 -0500 Subject: [PATCH 2/8] file setup, confirming css & js connected --- css/main.css | 4 ++++ index.html | 15 +++++++++++++++ js/main.js | 26 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/css/main.css b/css/main.css index e69de29..dbfd4b3 100644 --- a/css/main.css +++ b/css/main.css @@ -0,0 +1,4 @@ +body { + background-color: blue; + color: white; +} \ No newline at end of file diff --git a/index.html b/index.html index e69de29..05150a7 100644 --- a/index.html +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + + Tic Tac Toe + + + + Hello World + + + \ No newline at end of file diff --git a/js/main.js b/js/main.js index e69de29..b7bb83e 100644 --- a/js/main.js +++ b/js/main.js @@ -0,0 +1,26 @@ + /*----- constants -----*/ + + + + /*----- state variables -----*/ + +// Decide State Variables for Game: +// 1. Keep track of who's turn it is (turn: playerOne (1)/playerTwo (-1)) +// 2. Track board state (board: 2D array) +// null -> no player +// playerOne/playerTwo -> player at that cell +// 3. Variable for winner (winner: yes or no) +// null -> no winner / in play +// playerOne/playerTwo -> winner +// 'T' -> tie + + + /*----- cached elements -----*/ + + + + /*----- event listeners -----*/ + + + + /*----- functions -----*/ \ No newline at end of file From 5fb9b1345acc0b697149e4165cef7e08714fd51b Mon Sep 17 00:00:00 2001 From: lvlivingston Date: Sat, 30 Sep 2023 16:06:54 -0500 Subject: [PATCH 3/8] setup html --- index.html | 20 +++++++++++++++++++- js/main.js | 4 ++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 05150a7..1b75a40 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,25 @@ - Hello World +
Tic-Tac-Toe Game
+ +

Player One's turn

+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/js/main.js b/js/main.js index b7bb83e..3da2ed1 100644 --- a/js/main.js +++ b/js/main.js @@ -5,10 +5,10 @@ /*----- state variables -----*/ // Decide State Variables for Game: -// 1. Keep track of who's turn it is (turn: playerOne (1)/playerTwo (-1)) +// 1. Keep track of who's turn it is (turn: Player One (1)/ Player Two (-1)) // 2. Track board state (board: 2D array) // null -> no player -// playerOne/playerTwo -> player at that cell +// Player One (1) / Player Two (-1) -> player at that cell // 3. Variable for winner (winner: yes or no) // null -> no winner / in play // playerOne/playerTwo -> winner From 34ffcc202ad1faf4947b2c9d9d68f64f680f2876 Mon Sep 17 00:00:00 2001 From: lvlivingston Date: Sat, 30 Sep 2023 16:16:06 -0500 Subject: [PATCH 4/8] setup css only to see board to start js piece MVP --- css/main.css | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/css/main.css b/css/main.css index dbfd4b3..8da998a 100644 --- a/css/main.css +++ b/css/main.css @@ -1,4 +1,39 @@ +* { + box-sizing: border-box; +} + body { - background-color: blue; + height: 100vh; + margin: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +#board { + display: grid; + grid-template-columns: repeat(3, 10vmin); + grid-template-rows: repeat(3, 10vmin); + gap: 1vmin; + margin-top: -2vmin; +} + +#board > div { + border: 0.1vmin solid lightgray; +} + +button { + margin-top: 4vmin; + padding: 2vmin; + font-size: 2vmin; + border-radius: 4vmin; + border: 0.1vmin solid gray; + border-color: darkgray; + color: gray; +} + +button:hover { color: white; + background-color: darkgray; } \ No newline at end of file From 37579b6435bd517c3fc190e9e316183153da2a39 Mon Sep 17 00:00:00 2001 From: lvlivingston Date: Sat, 30 Sep 2023 16:19:01 -0500 Subject: [PATCH 5/8] added a bit more css styling to make easier to code js --- css/main.css | 15 ++++++++++++++- index.html | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/css/main.css b/css/main.css index 8da998a..06b3f8a 100644 --- a/css/main.css +++ b/css/main.css @@ -5,18 +5,31 @@ body { height: 100vh; margin: 0; + font-family: 'Audiowide', cursive; + font-family: 'Open Sans', sans-serif; display: flex; flex-direction: column; justify-content: center; align-items: center; } +header { + font-size: 4vmin; + color: darkgray; + letter-spacing: 1vmin; +} + +h1 { + color: gray; + font-size: 3vmin; +} + #board { display: grid; grid-template-columns: repeat(3, 10vmin); grid-template-rows: repeat(3, 10vmin); gap: 1vmin; - margin-top: -2vmin; + margin-top: 2vmin; } #board > div { diff --git a/index.html b/index.html index 1b75a40..8e2e343 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,9 @@ - + + + Tic Tac Toe From 08a3cf5c18b7487611375f454f15ef6d3952ffc8 Mon Sep 17 00:00:00 2001 From: lvlivingston Date: Sat, 30 Sep 2023 17:56:00 -0500 Subject: [PATCH 6/8] completed js for a MVP... includes just enough html, css & js to be a working tic-tac-toe game, still needs css to show x's and o's --- css/main.css | 11 +++- index.html | 18 +++--- js/main.js | 152 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 167 insertions(+), 14 deletions(-) diff --git a/css/main.css b/css/main.css index 06b3f8a..3dad923 100644 --- a/css/main.css +++ b/css/main.css @@ -24,11 +24,20 @@ h1 { font-size: 3vmin; } +.cell { + transform: scale(0.7); +} + +.cell:hover { + transform: scale(0.9); + transition: transform 150ms ease-in; +} + #board { display: grid; grid-template-columns: repeat(3, 10vmin); grid-template-rows: repeat(3, 10vmin); - gap: 1vmin; + gap: .2vmin; margin-top: 2vmin; } diff --git a/index.html b/index.html index 8e2e343..8dbbd3b 100644 --- a/index.html +++ b/index.html @@ -16,17 +16,17 @@

Player One's turn

-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
diff --git a/js/main.js b/js/main.js index 3da2ed1..52bbf45 100644 --- a/js/main.js +++ b/js/main.js @@ -1,5 +1,9 @@ /*----- constants -----*/ - + const COLORS = { + '0': 'white', + '1': 'black', + '-1': 'red' + }; /*----- state variables -----*/ @@ -11,16 +15,156 @@ // Player One (1) / Player Two (-1) -> player at that cell // 3. Variable for winner (winner: yes or no) // null -> no winner / in play -// playerOne/playerTwo -> winner +// Player One (1) / Player Two (-1) -> winner // 'T' -> tie +let board; //this will be an array of 3 column arrays +let turn; //this will be Player One (1) & Player Two's turns (-1) +let winner; // null = no winner; 1 or -1 = winner; 'T' = tie game /*----- cached elements -----*/ - +const messageEl = document.querySelector('h1'); +const resetButton = document.querySelector('button'); +const markerEls = [...document.querySelectorAll('#board > .cell')]; /*----- event listeners -----*/ +document.querySelectorAll('#board > .cell').forEach(cell => { + cell.addEventListener('click', handleDrop); + }); +resetButton.addEventListener('click', init); + + + /*----- functions -----*/ +init(); + +// Initialize all state, then call render() +function init() { + //to visualize the board's mapping to the DOM, rotate the board array 90 degrees counter-clockwise + board = [ + [0, 0, 0], //this is column 0 + [0, 0, 0], //this is column 1 + [0, 0, 0], //this is column 2 + ]; + turn = 1; + winner = null; + render(); +} + +//in response to user interaction, update all impacted state, then call render(); +function handleDrop(event) { + const cellId = event.target.id; + const columnIdx = parseInt(cellId.charAt(1)); + const rowIdx = parseInt(cellId.charAt(3)); + // guards + if (columnIdx === undefined || rowIdx === undefined || winner) return; + // check if the cell is empty + if (board[columnIdx][rowIdx] !== 0) return; + // update board state with current player value + board[columnIdx][rowIdx] = turn; + // switch turns + turn *= -1; + // check for winner + winner = getWinner(columnIdx, rowIdx); + render(); +} + +//check for winner in board state & return null if no winner, 1/-1 if a player has won, or 'T' if there's a tie +function getWinner(columnIdx, rowIdx) { + const hasTie = board.every(row => row.every(cell => cell !== 0)); + if (hasTie) { + return 'T'; // Return 'T' to indicate a tie + } + return checkVerticalWin(columnIdx, rowIdx) || + checkHorizontalWin(columnIdx, rowIdx) || + checkDiagonalWinNESW(columnIdx, rowIdx) || + checkDiagonalWinNWSE(columnIdx, rowIdx); +} + +function checkVerticalWin(columnIdx, rowIdx) { + const adjacentCountUp = countAdjacent(columnIdx, rowIdx, 0, 1); + const adjacentCountDown = countAdjacent(columnIdx, rowIdx, 0, -1); + return (adjacentCountUp + adjacentCountDown) >= 2 ? board[columnIdx][rowIdx] : null; +} + +function checkHorizontalWin(columnIdx, rowIdx) { + const adjacentCountLeft = countAdjacent(columnIdx, rowIdx, -1, 0); + const adjacentCountRight = countAdjacent(columnIdx, rowIdx, 1, 0); + return (adjacentCountLeft + adjacentCountRight) >= 2 ? board[columnIdx][rowIdx] : null; +} + +function checkDiagonalWinNESW(columnIdx, rowIdx) { + const adjacentCountNE = countAdjacent(columnIdx, rowIdx, 1, 1); + const adjacentCountSW = countAdjacent(columnIdx, rowIdx, -1, -1); + return (adjacentCountNE + adjacentCountSW) >= 2 ? board[columnIdx][rowIdx] : null; +} + +function checkDiagonalWinNWSE(columnIdx, rowIdx) { + const adjacentCountNW = countAdjacent(columnIdx, rowIdx, -1, 1); + const adjacentCountSE = countAdjacent(columnIdx, rowIdx, 1, -1); + return (adjacentCountNW + adjacentCountSE) >= 2 ? board[columnIdx][rowIdx] : null; +} + +function countAdjacent(columnIdx, rowIdx, columnOffset, rowOffset) { + //shortcut variable to the player value + const player = board[columnIdx][rowIdx]; + //track count of adjacent cells with the same player value + let count = 0; + //initialize new coordinates + columnIdx += columnOffset; + rowIdx += rowOffset; + while ( + //ensure columnIdx is within bounds of the board array + board[columnIdx] !== undefined && + board[columnIdx][rowIdx] !== undefined && + board[columnIdx][rowIdx] === player + ) { + count++; + columnIdx += columnOffset; + rowIdx += rowOffset; + } + return count; +} + +// Visualize all state in the DOM +function render() { + renderBoard(); + renderMessage(); + //hide/show UI elements (controls) + renderControls(); +} +function renderBoard() { + board.forEach(function (columnArr, columnIdx) { + //iterate over the cells in the current column (columnArr) + columnArr.forEach(function(cellValue, rowIdx) { + const cellId = `c${columnIdx}r${rowIdx}`; + const cellEl = document.getElementById(cellId); + cellEl.style.backgroundColor = COLORS[cellValue]; + }); + }); +} +function renderMessage() { + if (winner === 'T') { + messageEl.innerText = "It's a tie!!!"; + } else if (winner) { + //message for winner + messageEl.innerHTML = `${COLORS[winner].toUpperCase()} wins!`; + } else { + //message when game is in play + messageEl.innerHTML = `It's your turn, ${COLORS[turn].toUpperCase()}.`; + } +} - /*----- functions -----*/ \ No newline at end of file +function renderControls() { + resetButton.style.visibility = winner ? 'visible' : 'hidden'; + board.forEach(function (columnArr, columnIdx) { + // iterate over the cells in the current column (columnArr) + columnArr.forEach(function(cellValue, rowIdx) { + const cellId = `c${columnIdx}r${rowIdx}`; + const cellEl = document.getElementById(cellId); + cellEl.style.backgroundColor = COLORS[cellValue]; + }); + }); +} \ No newline at end of file From 88c0e5246a1002a06f09dde70b4f7661326633e7 Mon Sep 17 00:00:00 2001 From: lvlivingston Date: Sat, 30 Sep 2023 18:11:26 -0500 Subject: [PATCH 7/8] MVP where the code shows an X & an O --- js/main.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/js/main.js b/js/main.js index 52bbf45..e5f3f03 100644 --- a/js/main.js +++ b/js/main.js @@ -1,11 +1,16 @@ /*----- constants -----*/ - const COLORS = { - '0': 'white', - '1': 'black', - '-1': 'red' + // const COLORS = { + // '0': 'white', + // '1': 'black', + // '-1': 'red' + // }; + + const VALUE = { + '0': ' ', + '1': 'X', + '-1': 'O' }; - /*----- state variables -----*/ // Decide State Variables for Game: @@ -140,7 +145,8 @@ function renderBoard() { columnArr.forEach(function(cellValue, rowIdx) { const cellId = `c${columnIdx}r${rowIdx}`; const cellEl = document.getElementById(cellId); - cellEl.style.backgroundColor = COLORS[cellValue]; +// cellEl.style.backgroundColor = COLORS[cellValue]; + cellEl.innerText = VALUE[cellValue]; }); }); } @@ -150,10 +156,12 @@ function renderMessage() { messageEl.innerText = "It's a tie!!!"; } else if (winner) { //message for winner - messageEl.innerHTML = `${COLORS[winner].toUpperCase()} wins!`; + // messageEl.innerHTML = `${COLORS[winner].toUpperCase()} wins!`; + messageEl.innerHTML = `${VALUE[winner].toUpperCase()} wins!`; } else { //message when game is in play - messageEl.innerHTML = `It's your turn, ${COLORS[turn].toUpperCase()}.`; + // messageEl.innerHTML = `It's your turn, ${COLORS[turn].toUpperCase()}.`; + messageEl.innerHTML = `It's your turn, ${VALUE[turn].toUpperCase()}.`; } } @@ -164,7 +172,8 @@ function renderControls() { columnArr.forEach(function(cellValue, rowIdx) { const cellId = `c${columnIdx}r${rowIdx}`; const cellEl = document.getElementById(cellId); - cellEl.style.backgroundColor = COLORS[cellValue]; + // cellEl.style.backgroundColor = COLORS[cellValue]; + cellEl.style.backgroundColor = VALUE[cellValue]; }); }); } \ No newline at end of file From 4c5225aeeb81a0b0ddb5dc785708a412f89f72c1 Mon Sep 17 00:00:00 2001 From: lvlivingston Date: Sat, 30 Sep 2023 18:14:13 -0500 Subject: [PATCH 8/8] changed styling for shippable tic-tac-toe game... definitely a minimum viable product now --- css/main.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/css/main.css b/css/main.css index 3dad923..76a44fa 100644 --- a/css/main.css +++ b/css/main.css @@ -26,6 +26,9 @@ h1 { .cell { transform: scale(0.7); + display: flex; + justify-content: center; + align-items: center; } .cell:hover {