├── .vscode └── settings.json ├── index.html ├── scripts.js └── styles.css /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } 4 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | EM PROGRESSO - Jogo da Velha 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 |

X Venceu!

25 | 28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /scripts.js: -------------------------------------------------------------------------------- 1 | const cellElements = document.querySelectorAll("[data-cell]"); 2 | const board = document.querySelector("[data-board]"); 3 | const winningMessageTextElement = document.querySelector( 4 | "[data-winning-message-text]" 5 | ); 6 | const winningMessage = document.querySelector("[data-winning-message]"); 7 | const restartButton = document.querySelector("[data-restart-button]"); 8 | 9 | let isCircleTurn; 10 | 11 | const winningCombinations = [ 12 | [0, 1, 2], 13 | [3, 4, 5], 14 | [6, 7, 8], 15 | [0, 3, 6], 16 | [1, 4, 7], 17 | [2, 5, 8], 18 | [0, 4, 8], 19 | [2, 4, 6], 20 | ]; 21 | 22 | const startGame = () => { 23 | isCircleTurn = false; 24 | 25 | for (const cell of cellElements) { 26 | cell.classList.remove("circle"); 27 | cell.classList.remove("x"); 28 | cell.removeEventListener("click", handleClick); 29 | cell.addEventListener("click", handleClick, { once: true }); 30 | } 31 | 32 | setBoardHoverClass(); 33 | winningMessage.classList.remove("show-winning-message"); 34 | }; 35 | 36 | const endGame = (isDraw) => { 37 | if (isDraw) { 38 | winningMessageTextElement.innerText = "Empate!"; 39 | } else { 40 | winningMessageTextElement.innerText = isCircleTurn 41 | ? "O Venceu!" 42 | : "X Venceu!"; 43 | } 44 | 45 | winningMessage.classList.add("show-winning-message"); 46 | }; 47 | 48 | const checkForWin = (currentPlayer) => { 49 | return winningCombinations.some((combination) => { 50 | return combination.every((index) => { 51 | return cellElements[index].classList.contains(currentPlayer); 52 | }); 53 | }); 54 | }; 55 | 56 | const checkForDraw = () => { 57 | return [...cellElements].every((cell) => { 58 | return cell.classList.contains("x") || cell.classList.contains("circle"); 59 | }); 60 | }; 61 | 62 | const placeMark = (cell, classToAdd) => { 63 | cell.classList.add(classToAdd); 64 | }; 65 | 66 | const setBoardHoverClass = () => { 67 | board.classList.remove("circle"); 68 | board.classList.remove("x"); 69 | 70 | if (isCircleTurn) { 71 | board.classList.add("circle"); 72 | } else { 73 | board.classList.add("x"); 74 | } 75 | }; 76 | 77 | const swapTurns = () => { 78 | isCircleTurn = !isCircleTurn; 79 | 80 | setBoardHoverClass(); 81 | }; 82 | 83 | const handleClick = (e) => { 84 | // Colocar a marca (X ou Círculo) 85 | const cell = e.target; 86 | const classToAdd = isCircleTurn ? "circle" : "x"; 87 | 88 | placeMark(cell, classToAdd); 89 | 90 | // Verificar por vitória 91 | const isWin = checkForWin(classToAdd); 92 | 93 | // Verificar por empate 94 | const isDraw = checkForDraw(); 95 | 96 | if (isWin) { 97 | endGame(false); 98 | } else if (isDraw) { 99 | endGame(true); 100 | } else { 101 | // Mudar símbolo 102 | swapTurns(); 103 | } 104 | }; 105 | 106 | startGame(); 107 | 108 | restartButton.addEventListener("click", startGame); 109 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | height: 100vh; 9 | width: 100vw; 10 | background: linear-gradient( 11 | 90deg, 12 | rgba(65, 185, 131, 1) 0%, 13 | rgba(0, 212, 255, 1) 100% 14 | ); 15 | } 16 | 17 | .board { 18 | display: grid; 19 | width: 100%; 20 | height: 100%; 21 | display: grid; 22 | justify-content: center; 23 | align-content: center; 24 | justify-items: center; 25 | align-items: center; 26 | grid-template-columns: repeat(3, auto); 27 | } 28 | 29 | .board.x .cell:not(.x):not(.circle):hover::after, 30 | .board.x .cell:not(.x):not(.circle):hover::before, 31 | .board.circle .cell:not(.x):not(.circle):hover::after, 32 | .board.x .cell:not(.x):not(.circle):hover::before { 33 | background: rgba(255, 255, 255, 0.3) !important; 34 | } 35 | 36 | /* Célula */ 37 | .cell { 38 | width: 100px; 39 | height: 100px; 40 | border: 2px solid white; 41 | display: flex; 42 | justify-content: center; 43 | align-items: center; 44 | position: relative; 45 | } 46 | 47 | .cell.x, 48 | .cell.circle { 49 | cursor: not-allowed; 50 | } 51 | 52 | .cell:nth-child(1), 53 | .cell:nth-child(2), 54 | .cell:nth-child(3) { 55 | border-top: none; 56 | } 57 | 58 | .cell:nth-child(1), 59 | .cell:nth-child(4), 60 | .cell:nth-child(7) { 61 | border-left: none; 62 | } 63 | 64 | .cell:nth-child(7), 65 | .cell:nth-child(8), 66 | .cell:nth-child(9) { 67 | border-bottom: none; 68 | } 69 | 70 | .cell:nth-child(3), 71 | .cell:nth-child(6), 72 | .cell:nth-child(9) { 73 | border-right: none; 74 | } 75 | 76 | /* X */ 77 | .cell.x::before, 78 | .cell.x::after, 79 | .board.x .cell:not(.x):not(.circle):hover::after, 80 | .board.x .cell:not(.x):not(.circle):hover::before { 81 | content: ""; 82 | height: calc(100px * 0.15); 83 | width: calc(100px * 0.9); 84 | background: white; 85 | position: absolute; 86 | } 87 | 88 | .cell.x::before, 89 | .board.x .cell:not(.x):not(.circle):hover::before { 90 | transform: rotate(45deg); 91 | } 92 | 93 | .cell.x::after, 94 | .board.x .cell:not(.x):not(.circle):hover::after { 95 | transform: rotate(-45deg); 96 | } 97 | 98 | /* Circle */ 99 | .cell.circle::before, 100 | .cell.circle::after, 101 | .board.circle .cell:not(.x):not(.circle):hover::after, 102 | .board.circle .cell:not(.x):not(.circle):hover::after { 103 | content: ""; 104 | height: calc(100px * 0.9); 105 | width: calc(100px * 0.9); 106 | background: white; 107 | position: absolute; 108 | border-radius: 50%; 109 | } 110 | 111 | /* Mensagem de Vitória */ 112 | .winning-message { 113 | display: none; 114 | position: fixed; 115 | top: 0; 116 | left: 0; 117 | right: 0; 118 | bottom: 0; 119 | justify-content: center; 120 | align-items: center; 121 | background-color: rgba(0, 0, 0, 0.8); 122 | flex-direction: column; 123 | } 124 | 125 | .winning-message-button { 126 | font-size: 2.5rem; 127 | background-color: rgba(65, 185, 131, 1); 128 | padding: 10px 15px; 129 | cursor: pointer; 130 | border-radius: 5px; 131 | border: none; 132 | margin-top: 16px; 133 | color: white; 134 | } 135 | 136 | .winning-message-button:hover { 137 | color: rgba(65, 185, 131, 1); 138 | background-color: white; 139 | } 140 | 141 | .winning-message-text { 142 | color: white; 143 | font-size: 5rem; 144 | } 145 | 146 | .show-winning-message { 147 | display: flex; 148 | } 149 | --------------------------------------------------------------------------------