Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Tic Tac Tango Toe</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: '#3B82F6', | |
| secondary: '#10B981', | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| .game-cell { | |
| transition: all 0.3s ease; | |
| } | |
| .game-cell:hover { | |
| transform: scale(1.05); | |
| } | |
| .history-item:hover { | |
| background-color: rgba(59, 130, 246, 0.1); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div id="app" class="container mx-auto px-4 py-8"> | |
| <header class="text-center mb-10"> | |
| <h1 class="text-4xl md:text-5xl font-bold text-primary mb-2">Tic Tac Tango Toe</h1> | |
| <p class="text-gray-600">The game where X and O dance! 🕺</p> | |
| </header> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
| <!-- Game Board --> | |
| <div class="lg:col-span-2"> | |
| <div class="bg-white rounded-xl shadow-lg p-6"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800">Game Board</h2> | |
| <div class="flex items-center space-x-2"> | |
| <span class="px-3 py-1 bg-primary text-white rounded-full text-sm">Player X</span> | |
| <span class="px-3 py-1 bg-secondary text-white rounded-full text-sm">Player O</span> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-3 gap-3 mb-6"> | |
| <div v-for="(cell, index) in board" :key="index" | |
| @click="makeMove(index)" | |
| class="game-cell aspect-square flex items-center justify-center text-5xl font-bold rounded-lg bg-gray-100 hover:bg-gray-200 cursor-pointer transition-colors" | |
| :class="{ | |
| 'text-primary': cell === 'X', | |
| 'text-secondary': cell === 'O' | |
| }"> | |
| {{ cell }} | |
| </div> | |
| </div> | |
| <div class="flex justify-center"> | |
| <button @click="resetGame" | |
| class="px-6 py-2 bg-gray-800 text-white rounded-lg hover:bg-gray-700 transition-colors flex items-center space-x-2"> | |
| <i data-feather="refresh-cw" class="w-4 h-4"></i> | |
| <span>Reset Game</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Game Info --> | |
| <div class="space-y-6"> | |
| <!-- Score Board --> | |
| <div class="bg-white rounded-xl shadow-lg p-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800 mb-4">Score Board</h2> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div class="text-center p-4 bg-primary bg-opacity-10 rounded-lg"> | |
| <p class="text-sm text-primary font-medium">Player X</p> | |
| <p class="text-3xl font-bold text-primary">{{ scores.X }}</p> | |
| </div> | |
| <div class="text-center p-4 bg-secondary bg-opacity-10 rounded-lg"> | |
| <p class="text-sm text-secondary font-medium">Player O</p> | |
| <p class="text-3xl font-bold text-secondary">{{ scores.O }}</p> | |
| </div> | |
| <div class="col-span-2 text-center p-4 bg-gray-100 rounded-lg"> | |
| <p class="text-sm text-gray-600 font-medium">Ties</p> | |
| <p class="text-3xl font-bold text-gray-800">{{ scores.ties }}</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Game History --> | |
| <div class="bg-white rounded-xl shadow-lg p-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800 mb-4">Game History</h2> | |
| <div class="space-y-2 max-h-64 overflow-y-auto"> | |
| <div v-for="(record, index) in history" :key="index" | |
| class="history-item p-3 rounded-lg cursor-pointer border border-gray-100 hover:border-primary transition-colors" | |
| @click="replayGame(index)"> | |
| <div class="flex justify-between items-center"> | |
| <span class="font-medium text-gray-700">Game {{ index + 1 }}</span> | |
| <span class="text-sm" :class="{ | |
| 'text-primary': record.winner === 'X', | |
| 'text-secondary': record.winner === 'O', | |
| 'text-gray-500': record.winner === 'tie' | |
| }"> | |
| {{ record.winner === 'tie' ? 'Tie' : `Winner: ${record.winner}` }} | |
| </span> | |
| </div> | |
| </div> | |
| <p v-if="history.length === 0" class="text-gray-500 text-center py-4">No games played yet</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Current Player --> | |
| <div class="mt-8 text-center"> | |
| <p class="text-lg"> | |
| Current player: | |
| <span class="font-bold" :class="{ | |
| 'text-primary': currentPlayer === 'X', | |
| 'text-secondary': currentPlayer === 'O' | |
| }"> | |
| {{ currentPlayer }} | |
| </span> | |
| </p> | |
| <p v-if="gameStatus !== 'ongoing'" class="text-xl font-bold mt-2" :class="{ | |
| 'text-primary': gameStatus === 'X', | |
| 'text-secondary': gameStatus === 'O', | |
| 'text-gray-600': gameStatus === 'tie' | |
| }"> | |
| {{ gameStatus === 'tie' ? "It's a tie!" : `Player ${gameStatus} wins!` }} | |
| </p> | |
| </div> | |
| </div> | |
| <script src="https://cdn.jsdelivr.net/npm/vue@3.2.31/dist/vue.global.min.js"></script> | |
| <script> | |
| feather.replace(); | |
| const { createApp, ref, computed } = Vue; | |
| createApp({ | |
| setup() { | |
| const board = ref(Array(9).fill(null)); | |
| const currentPlayer = ref('X'); | |
| const gameStatus = ref('ongoing'); | |
| const history = ref([]); | |
| const scores = ref({ | |
| X: 0, | |
| O: 0, | |
| ties: 0 | |
| }); | |
| const winningCombinations = [ | |
| [0, 1, 2], [3, 4, 5], [6, 7, 8], // rows | |
| [0, 3, 6], [1, 4, 7], [2, 5, 8], // columns | |
| [0, 4, 8], [2, 4, 6] // diagonals | |
| ]; | |
| const checkWinner = () => { | |
| for (const combo of winningCombinations) { | |
| const [a, b, c] = combo; | |
| if (board.value[a] && board.value[a] === board.value[b] && board.value[a] === board.value[c]) { | |
| return board.value[a]; | |
| } | |
| } | |
| return board.value.includes(null) ? null : 'tie'; | |
| }; | |
| const makeMove = (index) => { | |
| if (board.value[index] || gameStatus.value !== 'ongoing') return; | |
| board.value[index] = currentPlayer.value; | |
| const winner = checkWinner(); | |
| if (winner) { | |
| gameStatus.value = winner; | |
| if (winner !== 'tie') { | |
| scores.value[winner]++; | |
| } else { | |
| scores.value.ties++; | |
| } | |
| history.value.push({ | |
| board: [...board.value], | |
| winner: winner | |
| }); | |
| } else { | |
| currentPlayer.value = currentPlayer.value === 'X' ? 'O' : 'X'; | |
| } | |
| }; | |
| const resetGame = () => { | |
| board.value = Array(9).fill(null); | |
| currentPlayer.value = 'X'; | |
| gameStatus.value = 'ongoing'; | |
| }; | |
| const replayGame = (index) => { | |
| const game = history.value[index]; | |
| board.value = [...game.board]; | |
| gameStatus.value = game.winner; | |
| currentPlayer.value = game.winner === 'tie' ? 'X' : game.winner === 'X' ? 'O' : 'X'; | |
| }; | |
| return { | |
| board, | |
| currentPlayer, | |
| gameStatus, | |
| history, | |
| scores, | |
| makeMove, | |
| resetGame, | |
| replayGame | |
| }; | |
| } | |
| }).mount('#app'); | |
| </script> | |
| <script>feather.replace();</script> | |
| </body> | |
| </html> | |