mirror of
https://gitea.smigz.com/smiggiddy/odin-codeprojects.git
synced 2024-12-25 06:00:43 -05:00
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:
parent
8822ecfc5f
commit
87be725167
3 changed files with 412 additions and 0 deletions
86
tictactoe/css/style.css
Normal file
86
tictactoe/css/style.css
Normal 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
35
tictactoe/index.html
Normal 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
291
tictactoe/js/main.js
Normal 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();
|
||||
})();
|
||||
|
||||
|
Loading…
Reference in a new issue