├── .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 |
--------------------------------------------------------------------------------