From 87be725167c3ca3162ba42f80466cf791151ea23 Mon Sep 17 00:00:00 2001
From: Smig <89040888+smiggiddy@users.noreply.github.com>
Date: Thu, 14 Dec 2023 18:37:25 -0500
Subject: [PATCH] Realtictactoe (#8)
* feat: setup initial game methods
* feat: add update position method
* feat: created basic prompt for input
* feat: cli game loop complete
* feat: boiler plate html added
* feat: added gameplay
* feat: added game evaluation
* feat: added grid and basic styling
* feat: game functionality added
* feat: project complete
---
tictactoe/css/style.css | 86 ++++++++++++
tictactoe/index.html | 35 +++++
tictactoe/js/main.js | 291 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 412 insertions(+)
create mode 100644 tictactoe/css/style.css
create mode 100644 tictactoe/index.html
create mode 100644 tictactoe/js/main.js
diff --git a/tictactoe/css/style.css b/tictactoe/css/style.css
new file mode 100644
index 0000000..a305a85
--- /dev/null
+++ b/tictactoe/css/style.css
@@ -0,0 +1,86 @@
+:root {
+ font-size: 32px;
+ font-family: 'Roboto', sans-serif;
+ --dark-color: #607274;
+ --light-color: #FAEED1;
+ --gray-color: #F4F4F2;
+ --light-grey-color: #E8E8E8;
+}
+
+body,html {
+ min-width: 100vw;
+ margin: 0;
+ padding: 0;
+}
+
+header {
+ display: flex;
+ min-width: 100vw;
+ justify-content: center;
+ align-content: center;
+ background-color: var(--dark-color);
+ color: var(--gray-color);
+}
+
+
+header h1 {
+ font-size: 2rem;
+}
+
+
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background-color: var(--light-grey-color);
+ height: 100vh;
+}
+
+.content {
+ display: flex;
+ justify-content: space-between;
+ align-content: center;
+ flex-direction: column;
+ gap: 20px
+}
+
+.game-status {
+ display: flex;
+ justify-content: center;
+ align-content: center;
+}
+
+.content>.game-area {
+ display: grid;
+ grid-area: 1 / 1 / 4 / 4;
+ grid-template-rows: repeat(3, 150px);
+ grid-template-columns: repeat(3, 150px);
+ justify-items: center;
+ align-items: center;
+ border: solid;
+}
+
+.block {
+ font-family: 'Silkscreen', sans-serif;
+ font-weight: 700;
+ height: 150px;
+ width: 150px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ border: solid;
+ font-size: 3rem;
+}
+
+.block:hover {
+ cursor: pointer;
+}
+
+.btn {
+ padding: 16px 20px;
+ font-size: 0.5rem;
+ width: 200px;
+ align-self: center;
+ cursor: pointer;
+}
diff --git a/tictactoe/index.html b/tictactoe/index.html
new file mode 100644
index 0000000..c858046
--- /dev/null
+++ b/tictactoe/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+ Tic-Tac-Toe
+
+
+
+
+
+
diff --git a/tictactoe/js/main.js b/tictactoe/js/main.js
new file mode 100644
index 0000000..70c4441
--- /dev/null
+++ b/tictactoe/js/main.js
@@ -0,0 +1,291 @@
+const tictactoeBoard = (function() {
+
+ let gameBoard = new Array(["", "", ""], ["", "", ""], ["", "", ""]);
+
+ const getGameBoard = () => gameBoard;
+ // const printGameBoard = () => console.log(gameBoard[0] + "\n" + gameBoard[1] + "\n" + gameBoard[2]);
+
+ const resetGameBoard = () => {
+ gameBoard.forEach(item => {
+ item[0] = '';
+ item[1] = '';
+ item[2] = '';
+ })
+ }
+
+ const posititionAvailable = (position) => {
+ switch (position) {
+ case 1:
+ return isEmpty(gameBoard[0][0]);
+ case 2:
+ return isEmpty(gameBoard[0][1]);
+ case 3:
+ return isEmpty(gameBoard[0][2]);
+ case 4:
+ return isEmpty(gameBoard[1][0]);
+ case 5:
+ return isEmpty(gameBoard[1][1]);
+ case 6:
+ return isEmpty(gameBoard[1][2]);
+ case 7:
+ return isEmpty(gameBoard[2][0]);
+ case 8:
+ return isEmpty(gameBoard[2][1]);
+ case 9:
+ return isEmpty(gameBoard[2][2]);
+ default:
+ Alert('Must enter a number 1 - 9');
+ break;
+ }
+ }
+
+ const isEmpty = position => position === "";
+
+ const placeOnBoard = (position, marker) => {
+ // places marker on the gameboard
+ marker = marker.getValue(); // get value from object
+
+ switch (position) {
+ case 1:
+ gameBoard[0][0] = marker;
+ break;
+ case 2:
+ gameBoard[0][1] = marker;
+ break;
+ case 3:
+ gameBoard[0][2] = marker;
+ break;
+ case 4:
+ gameBoard[1][0] = marker;
+ break;
+ case 5:
+ gameBoard[1][1] = marker;
+ break;
+ case 6:
+ gameBoard[1][2] = marker;
+ break;
+ case 7:
+ gameBoard[2][0] = marker;
+ break;
+ case 8:
+ gameBoard[2][1] = marker;
+ break;
+ case 9:
+ gameBoard[2][2] = marker;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return {
+ resetGameBoard,
+ // printGameBoard,
+ getGameBoard,
+ placeOnBoard,
+ posititionAvailable
+ };
+})();
+
+
+const playerInfo = (function() {
+ class Player {
+ constructor(name, piece) {
+ this.piece = piece;
+ this.name = name;
+ }
+ }
+
+ const player = (name, piece) => {
+ return new Player(name, piece);
+ };
+
+ return { player }
+})();
+
+
+function block() {
+ value = "-";
+ const addToken = player => {
+ // player is a player info object
+ value = player.piece;
+ };
+
+ const getValue = () => value;
+
+ return {
+ addToken,
+ getValue
+ };
+};
+
+const gameController = (() => {
+ // Controls the overall flow of the game
+ let players = [
+ playerInfo.player('Player 1', 'X'),
+ playerInfo.player('Player 2', 'O')
+ ]
+
+ let gameOver;
+ let xTurn = true; // True = X's turn, False = O's turn
+ let round = 0; // 9 Total Rounds for Tic Tac Toe
+
+ const resetGame = () => {
+ round = 0;
+ xTurn = true;
+ gameOver = false;
+ tictactoeBoard.resetGameBoard();
+ }
+
+ const gameStatus = () => gameOver;
+ const gameRound = () => round;
+
+ const switchPlayerTurn = () => {
+ xTurn = !xTurn;
+ }
+
+ const playRound = position => {
+ let marker = new block()
+ let positionOpen = tictactoeBoard.posititionAvailable(position);
+
+ if (round < 10 && positionOpen) {
+
+ let player = xTurn ? players[0] : players[1];
+ marker.addToken(player);
+ tictactoeBoard.placeOnBoard(position, marker); // position on board
+
+ // Check for Winner
+ gameOver = evalGameOutcome(tictactoeBoard.getGameBoard());
+
+ if (gameOver) {
+ console.log(`${player.name}: ${player.piece} won!`)
+ return
+ }
+ switchPlayerTurn();
+
+ } else {
+ return
+ }
+ round += 1;
+ }
+
+ const evalGameOutcome = gameBoard => {
+ // create a new array of the vertical indexes in the game board
+ let vertArray = [];
+ gameBoard.forEach((_, index) => vertArray.push(gameBoard.map(e => e[index])));
+
+ // create an array of diagnal pieces
+ let diagArray = [
+ [gameBoard[0][0], gameBoard[1][1], gameBoard[2][2]],
+ [gameBoard[0, 2], gameBoard[1][1], gameBoard[2][0]]
+ ];
+
+ let checkGameBoard = [gameBoard, diagArray, vertArray];
+ for (i = 0; i < 3; i++) {
+ let outcome = _evalGameOutcome(checkGameBoard[i]);
+ if (outcome) {
+ return true;
+ };
+ };
+ return false;
+ };
+
+ const _evalGameOutcome = arr => {
+ // Checks if array values are equal to determine winner
+ let isWinner = checkArr => checkArr.reduce(function(a, b) { return a === b ? a : false; });
+
+ const outcome = arr.find(element => {
+ let result = isWinner(element);
+ if (result != false) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+ return outcome;
+ };
+
+ return { playRound, resetGame, gameStatus, gameRound }
+})();
+
+
+const screenController = (() => {
+ const contentDiv = document.querySelector('.content')
+ const gameDiv = document.querySelector('.game-area');
+ const gameStatus = document.querySelector('.game-status')
+ const game = tictactoeBoard.getGameBoard();
+ const resetButton = document.createElement('button');
+
+ resetButton.classList.add(['btn']);
+ resetButton.textContent = 'New Game';
+ contentDiv.appendChild(resetButton);
+
+ const updateScreen = () => {
+ status()
+ displayBoard();
+ }
+
+ const status = () => {
+ let pTags = gameStatus.querySelectorAll('p');
+ if (pTags) pTags.forEach(e => e.remove());
+
+ let message = document.createElement('p');
+ let gameOn = gameController.gameStatus() ? "Play again?" : "";
+ let round = gameController.gameRound();
+
+ if (round <= 9 && gameOn) {
+ message.textContent = gameOn;
+ } else if (round >= 9 && !gameOn) {
+ message.textContent = "Draw, play again?";
+ }
+
+ gameStatus.appendChild(message);
+
+ }
+
+
+ const displayBoard = () => {
+ // Check if someone won the game
+
+ let count = 0; // This is for the data blocks
+ gameDiv.querySelectorAll('.block').forEach(e => e.remove());
+ game.forEach((e) => {
+ e.forEach((element) => {
+ count += 1;
+ const block = document.createElement('div');
+ block.classList.add('block');
+ block.setAttribute('data-block', count);
+ block.textContent = element;
+ block.addEventListener('click', clickHandlerBoard);
+ gameDiv.appendChild(block);
+ });
+ });
+ }
+
+
+
+ function clickHandlerBoard(e) {
+ const selectedBlock = e.target.dataset.block;
+ const empty = e.target.textContent;
+ const gameStatus = gameController.gameStatus();
+
+ if (!selectedBlock || empty !== "" || gameStatus) return;
+
+ gameController.playRound(Number(selectedBlock));
+ updateScreen();
+ };
+
+ function clickResetButton() {
+ gameController.resetGame();
+ status();
+ updateScreen();
+ }
+
+ resetButton.addEventListener('click', clickResetButton);
+
+
+ // Initial Load
+ updateScreen();
+})();
+
+