├── favicon.ico ├── src ├── images │ ├── desktop.png │ └── mobile.png ├── css │ └── style.css └── js │ └── script.js ├── index.html └── README.md /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Iqbolshoh/javascript-xo-game/HEAD/favicon.ico -------------------------------------------------------------------------------- /src/images/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Iqbolshoh/javascript-xo-game/HEAD/src/images/desktop.png -------------------------------------------------------------------------------- /src/images/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Iqbolshoh/javascript-xo-game/HEAD/src/images/mobile.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | X O Game 11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | min-height: 100vh; 8 | background-color: #f0f4f8; 9 | font-family: 'Arial', sans-serif; 10 | flex-direction: column; 11 | box-sizing: border-box; 12 | } 13 | 14 | .board { 15 | display: grid; 16 | grid-template-columns: repeat(3, 1fr); 17 | grid-template-rows: repeat(3, 1fr); 18 | gap: 8px; 19 | max-width: 350px; 20 | max-height: 350px; 21 | width: 100%; 22 | height: 100vh; 23 | background-color: #fff; 24 | padding: 10px; 25 | border-radius: 15px; 26 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 27 | } 28 | 29 | .cell { 30 | width: 100%; 31 | height: 100%; 32 | display: flex; 33 | align-items: center; 34 | justify-content: center; 35 | font-size: 36px; 36 | font-weight: bold; 37 | color: #333; 38 | background-color: #f9f9f9; 39 | border: 1px solid #ddd; 40 | border-radius: 8px; 41 | cursor: pointer; 42 | transition: background-color 0.3s, transform 0.3s ease, box-shadow 0.3s ease; 43 | } 44 | 45 | .cell:hover { 46 | background-color: #e0e0e0; 47 | transform: scale(1.1); 48 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.2); 49 | } 50 | 51 | #game-board { 52 | background-color: #f4f4f9; 53 | padding: 20px; 54 | border-radius: 15px; 55 | box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); 56 | margin-bottom: 20px; 57 | } 58 | 59 | #game-over-message { 60 | font-size: 20px; 61 | font-weight: bold; 62 | color: #333; 63 | margin-top: 15px; 64 | text-align: center; 65 | display: none; 66 | } 67 | 68 | #restart-button { 69 | margin-top: 20px; 70 | padding: 12px 18px; 71 | font-size: 18px; 72 | font-weight: 600; 73 | cursor: pointer; 74 | background-color: #4caf50; 75 | color: #fff; 76 | border: none; 77 | border-radius: 8px; 78 | transition: background-color 0.3s, transform 0.3s ease; 79 | } 80 | 81 | #restart-button:hover { 82 | background-color: #45a049; 83 | transform: scale(1.05); 84 | } 85 | 86 | @media screen and (max-width: 600px) { 87 | .board { 88 | max-width: 90%; 89 | grid-template-columns: repeat(3, 1fr); 90 | grid-template-rows: repeat(3, 1fr); 91 | } 92 | 93 | #game-board { 94 | padding: 15px; 95 | } 96 | 97 | #game-over-message { 98 | font-size: 18px; 99 | } 100 | 101 | #restart-button { 102 | font-size: 16px; 103 | padding: 10px 15px; 104 | } 105 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎮 JavaScript XO Game 2 | 3 | This is a **Tic-Tac-Toe (XO) game** implemented using **HTML, CSS, and JavaScript**. The game allows **two players** to compete on a **3x3 grid**. 4 | 5 | ## 👀 Preview 6 | 7 | ### 💻 **Desktop** 8 | ![Desktop Preview](./src/images/desktop.png) 9 | 10 | ### 📱 **Mobile** 11 | ![Mobile Preview](./src/images/mobile.png) 12 | 13 | ## 🚀 Features 14 | 15 | - ✅ **Responsive Design:** The game adapts to **all screen sizes**. 16 | - 🎮 **Interactive Game Board:** Players can **place X or O** by clicking on cells. 17 | - 🏆 **Winner Highlight:** The **winning combination** is highlighted in **green**. 18 | - 🤝 **Draw Detection:** If the game ends in a draw, a **message is displayed**. 19 | - 🔄 **Play Again Button:** A **restart button** allows players to reset the game and play again. 20 | 21 | ## 🛠️ Installation 22 | 23 | To set up and run the XO Game on your local machine: 24 | 25 | ### 1️⃣ **Clone the Repository** 26 | ```bash 27 | git clone https://github.com/Iqbolshoh/javascript-xo-game.git 28 | ``` 29 | ### 2️⃣ **Navigate to the Project Directory** 30 | ```bash 31 | cd javascript-xo-game 32 | ``` 33 | ### 3️⃣ **Open the `index.html` File** 34 | Simply open the `index.html` file in any modern **web browser** to start playing. 35 | 36 | ## 🖥 Technologies Used 37 | ![HTML](https://img.shields.io/badge/HTML-%23E34F26.svg?style=for-the-badge&logo=html5&logoColor=white) 38 | ![CSS](https://img.shields.io/badge/CSS-%231572B6.svg?style=for-the-badge&logo=css3&logoColor=white) 39 | ![JavaScript](https://img.shields.io/badge/JavaScript-%23F7DF1C.svg?style=for-the-badge&logo=javascript&logoColor=black) 40 | 41 | ## 📜 License 42 | This project is open-source and available under the **MIT License**. 43 | 44 | ## 🤝 Contributing 45 | 🎯 Contributions are welcome! If you have suggestions or want to enhance the project, feel free to fork the repository and submit a pull request. 46 | 47 | ## 📬 Connect with Me 48 | 💬 I love meeting new people and discussing tech, business, and creative ideas. Let’s connect! You can reach me on these platforms: 49 | 50 |
51 | 52 | 53 | 59 | 65 | 71 | 77 | 83 | 89 | 95 | 101 | 107 | 108 |
54 | 55 | Website 57 | 58 | 60 | 61 | Email 63 | 64 | 66 | 67 | GitHub 69 | 70 | 72 | 73 | LinkedIn 75 | 76 | 78 | 79 | Telegram 81 | 82 | 84 | 85 | WhatsApp 87 | 88 | 90 | 91 | Instagram 93 | 94 | 96 | 97 | X 99 | 100 | 102 | 103 | YouTube 105 | 106 |
109 |
110 | -------------------------------------------------------------------------------- /src/js/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", function () { 2 | const board = document.getElementById("game-board"); 3 | const gameOverMessage = document.getElementById("game-over-message"); 4 | const restartButton = document.getElementById("restart-button"); 5 | let currentPlayer = "X"; 6 | let gameOver = false; 7 | 8 | function createCell() { 9 | const cell = document.createElement("div"); 10 | cell.classList.add("cell"); 11 | cell.addEventListener("click", handleCellClick); 12 | return cell; 13 | } 14 | 15 | function handleCellClick(event) { 16 | if (gameOver || event.target.textContent !== "" || currentPlayer !== "X") { 17 | return; 18 | } 19 | 20 | event.target.textContent = currentPlayer; 21 | 22 | if (checkWinner()) { 23 | gameOverMessage.textContent = `${currentPlayer} wins!`; 24 | gameOverMessage.style.display = "block"; 25 | gameOver = true; 26 | animateWinner(); 27 | restartButton.style.display = "block"; 28 | return; 29 | } else { 30 | currentPlayer = "O"; 31 | } 32 | 33 | if (checkDraw()) { 34 | gameOverMessage.textContent = "It's a draw!"; 35 | gameOverMessage.style.display = "block"; 36 | gameOver = true; 37 | restartButton.style.display = "block"; 38 | } else if (!gameOver) { 39 | setTimeout(computerMove, 500); 40 | } 41 | } 42 | 43 | function computerMove() { 44 | const cells = document.querySelectorAll(".cell"); 45 | let availableCells = []; 46 | 47 | cells.forEach((cell, index) => { 48 | if (cell.textContent === "") { 49 | availableCells.push(cell); 50 | } 51 | }); 52 | 53 | if (availableCells.length > 0) { 54 | const bestMove = findBestMove(); 55 | bestMove.textContent = "O"; 56 | 57 | if (checkWinner()) { 58 | gameOverMessage.textContent = "O wins!"; 59 | gameOverMessage.style.display = "block"; 60 | gameOver = true; 61 | animateWinner(); 62 | restartButton.style.display = "block"; 63 | } else { 64 | currentPlayer = "X"; 65 | } 66 | 67 | if (checkDraw() && !gameOver) { 68 | gameOverMessage.textContent = "It's a draw!"; 69 | gameOverMessage.style.display = "block"; 70 | gameOver = true; 71 | restartButton.style.display = "block"; 72 | } 73 | } 74 | } 75 | 76 | function checkWinner() { 77 | const cells = document.querySelectorAll(".cell"); 78 | 79 | const winCombinations = [ 80 | [0, 1, 2], [3, 4, 5], [6, 7, 8], 81 | [0, 3, 6], [1, 4, 7], [2, 5, 8], 82 | [0, 4, 8], [2, 4, 6] 83 | ]; 84 | 85 | for (const combo of winCombinations) { 86 | const [a, b, c] = combo; 87 | if (cells[a].textContent && cells[a].textContent === cells[b].textContent && cells[b].textContent === cells[c].textContent) { 88 | highlightWinner(cells[a], cells[b], cells[c]); 89 | return true; 90 | } 91 | } 92 | 93 | return false; 94 | } 95 | 96 | function highlightWinner(cellA, cellB, cellC) { 97 | cellA.style.backgroundColor = "#8bc34a"; 98 | cellB.style.backgroundColor = "#8bc34a"; 99 | cellC.style.backgroundColor = "#8bc34a"; 100 | } 101 | 102 | function animateWinner() { 103 | const winningCells = document.querySelectorAll(".cell[style='background-color: #8bc34a;']"); 104 | 105 | winningCells.forEach(cell => { 106 | cell.style.transition = "transform 0.5s ease-in-out"; 107 | cell.style.transform = "scale(1.2)"; 108 | }); 109 | } 110 | 111 | function checkDraw() { 112 | const cells = document.querySelectorAll(".cell"); 113 | for (const cell of cells) { 114 | if (cell.textContent === "") { 115 | return false; 116 | } 117 | } 118 | return true; 119 | } 120 | 121 | function findBestMove() { 122 | const cells = document.querySelectorAll(".cell"); 123 | 124 | const winCombinations = [ 125 | [0, 1, 2], [3, 4, 5], [6, 7, 8], 126 | [0, 3, 6], [1, 4, 7], [2, 5, 8], 127 | [0, 4, 8], [2, 4, 6] 128 | ]; 129 | 130 | for (const combo of winCombinations) { 131 | const [a, b, c] = combo; 132 | const combination = [cells[a], cells[b], cells[c]]; 133 | 134 | if (combination.filter(cell => cell.textContent === "O").length === 2 && combination.some(cell => cell.textContent === "")) { 135 | return combination.find(cell => cell.textContent === ""); 136 | } 137 | 138 | if (combination.filter(cell => cell.textContent === "X").length === 2 && combination.some(cell => cell.textContent === "")) { 139 | return combination.find(cell => cell.textContent === ""); 140 | } 141 | } 142 | 143 | const center = cells[4]; 144 | if (center.textContent === "") { 145 | return center; 146 | } 147 | 148 | const availableCells = Array.from(cells).filter(cell => cell.textContent === ""); 149 | return availableCells[Math.floor(Math.random() * availableCells.length)]; 150 | } 151 | 152 | restartButton.addEventListener("click", function () { 153 | location.reload(); 154 | }); 155 | 156 | for (let i = 0; i < 9; i++) { 157 | board.appendChild(createCell()); 158 | } 159 | }); --------------------------------------------------------------------------------