├── .idea ├── .gitignore ├── misc.xml ├── vcs.xml ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml └── Tic-Tac-Toe.iml ├── .gitignore ├── README.md ├── style.css ├── LICENSE ├── index.html └── script.js /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac files 2 | .DS_Store 3 | 4 | # Dependency directories 5 | /node_modules 6 | */node_modules 7 | 8 | # lerna files 9 | /lerna-debug.log 10 | 11 | # Lock files 12 | /package-lock.json 13 | /yarn.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tic-Tac-Toe 2 | Repository for a [Dev.to article](https://dev.to/bornasepic/pure-and-simple-tic-tac-toe-with-javascript-4pgn) 3 | 4 | #### Typescript 5 | If you're interested in a type supported version of the game checkout the `typescript` branch and follow the instructions from there. 6 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/Tic-Tac-Toe.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Arial", sans-serif; 3 | } 4 | 5 | section { 6 | text-align: center; 7 | } 8 | 9 | .game--container { 10 | display: grid; 11 | grid-template-columns: repeat(3, auto); 12 | width: 306px; 13 | margin: 50px auto; 14 | } 15 | 16 | .cell { 17 | font-family: "Permanent Marker", cursive; 18 | width: 100px; 19 | height: 100px; 20 | box-shadow: 0 0 0 1px #333333; 21 | border: 1px solid #333333; 22 | cursor: pointer; 23 | 24 | line-height: 100px; 25 | font-size: 60px; 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Borna Šepić 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Tic Tac Toe 9 | 10 | 11 | 12 |
13 |

Tic Tac Toe

14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |

26 | 27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | const statusDisplay = document.querySelector('.game--status'); 2 | 3 | let gameActive = true; 4 | let currentPlayer = "X"; 5 | let gameState = ["", "", "", "", "", "", "", "", ""]; 6 | 7 | const winningMessage = () => `Player ${currentPlayer} has won!`; 8 | const drawMessage = () => `Game ended in a draw!`; 9 | const currentPlayerTurn = () => `It's ${currentPlayer}'s turn`; 10 | 11 | statusDisplay.innerHTML = currentPlayerTurn(); 12 | 13 | const winningConditions = [ 14 | [0, 1, 2], 15 | [3, 4, 5], 16 | [6, 7, 8], 17 | [0, 3, 6], 18 | [1, 4, 7], 19 | [2, 5, 8], 20 | [0, 4, 8], 21 | [2, 4, 6] 22 | ]; 23 | 24 | function handleCellPlayed(clickedCell, clickedCellIndex) { 25 | gameState[clickedCellIndex] = currentPlayer; 26 | clickedCell.innerHTML = currentPlayer; 27 | } 28 | 29 | function handlePlayerChange() { 30 | currentPlayer = currentPlayer === "X" ? "O" : "X"; 31 | statusDisplay.innerHTML = currentPlayerTurn(); 32 | } 33 | 34 | function handleResultValidation() { 35 | let roundWon = false; 36 | for (let i = 0; i <= 7; i++) { 37 | const winCondition = winningConditions[i]; 38 | let a = gameState[winCondition[0]]; 39 | let b = gameState[winCondition[1]]; 40 | let c = gameState[winCondition[2]]; 41 | if (a === '' || b === '' || c === '') { 42 | continue; 43 | } 44 | if (a === b && b === c) { 45 | roundWon = true; 46 | break 47 | } 48 | } 49 | 50 | if (roundWon) { 51 | statusDisplay.innerHTML = winningMessage(); 52 | gameActive = false; 53 | return; 54 | } 55 | 56 | let roundDraw = !gameState.includes(""); 57 | if (roundDraw) { 58 | statusDisplay.innerHTML = drawMessage(); 59 | gameActive = false; 60 | return; 61 | } 62 | 63 | handlePlayerChange(); 64 | } 65 | 66 | function handleCellClick(clickedCellEvent) { 67 | const clickedCell = clickedCellEvent.target; 68 | const clickedCellIndex = parseInt(clickedCell.getAttribute('data-cell-index')); 69 | 70 | if (gameState[clickedCellIndex] !== "" || !gameActive) { 71 | return; 72 | } 73 | 74 | handleCellPlayed(clickedCell, clickedCellIndex); 75 | handleResultValidation(); 76 | } 77 | 78 | function handleRestartGame() { 79 | gameActive = true; 80 | currentPlayer = "X"; 81 | gameState = ["", "", "", "", "", "", "", "", ""]; 82 | statusDisplay.innerHTML = currentPlayerTurn(); 83 | document.querySelectorAll('.cell').forEach(cell => cell.innerHTML = ""); 84 | } 85 | 86 | document.querySelectorAll('.cell').forEach(cell => cell.addEventListener('click', handleCellClick)); 87 | document.querySelector('.game--restart').addEventListener('click', handleRestartGame); --------------------------------------------------------------------------------