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
This commit is contained in:
Mike 2023-12-14 18:37:25 -05:00 committed by GitHub
parent 8822ecfc5f
commit 87be725167
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 412 additions and 0 deletions

86
tictactoe/css/style.css Normal file
View file

@ -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;
}

35
tictactoe/index.html Normal file
View file

@ -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">
<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:ital,wght@0,500;0,700;0,900;1,500;1,700;1,900&family=Silkscreen:wght@700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css">
<title>Tic-Tac-Toe</title>
</head>
<body>
<div class="container">
<header>
<div class="header">
<h1>Tic-Tac-Toe</h1>
</div>
</header>
<main>
<div class="content">
<div class="game-status">
</div>
<div class="game-area">
</div>
</div>
</main>
<footer>
<div class="footer"></div>
</footer>
</div>
<script src="./js/main.js"></script>
</body>
</html>

291
tictactoe/js/main.js Normal file
View file

@ -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();
})();