Spaces:
Sleeping
Sleeping
| class RoomTicTacToeGame { | |
| constructor() { | |
| this.currentRoomId = null; | |
| this.roomData = null; | |
| this.cells = document.querySelectorAll('.cell'); | |
| this.gameStatus = document.getElementById('gameStatus'); | |
| this.roomInfo = document.getElementById('roomInfo'); | |
| this.chatMessages = document.getElementById('chatMessages'); | |
| this.chatInput = document.getElementById('chatInput'); | |
| this.sendBtn = document.getElementById('sendBtn'); | |
| this.gameArea = document.getElementById('gameArea'); | |
| this.noRoom = document.getElementById('noRoom'); | |
| this.initGame(); | |
| } | |
| initGame() { | |
| this.cells.forEach((cell, index) => { | |
| cell.addEventListener('click', () => this.handleCellClick(index)); | |
| }); | |
| // Update room state every 2 seconds if in a room | |
| setInterval(() => { | |
| if (this.currentRoomId) { | |
| this.refreshRoomState(); | |
| } | |
| }, 2000); | |
| } | |
| async createNewRoom() { | |
| try { | |
| const response = await fetch('/rooms', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| } | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| this.joinRoomById(data.room_id); | |
| } catch (error) { | |
| console.error('Failed to create room:', error); | |
| this.gameStatus.textContent = "Failed to create room. Try again."; | |
| } | |
| } | |
| async joinRoom() { | |
| const roomId = document.getElementById('roomIdInput').value.trim(); | |
| if (!roomId) { | |
| this.gameStatus.textContent = "Please enter a room ID"; | |
| return; | |
| } | |
| await this.joinRoomById(roomId); | |
| } | |
| async joinRoomById(roomId) { | |
| try { | |
| const response = await fetch(`/rooms/${roomId}`); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| this.currentRoomId = roomId; | |
| this.roomData = data.room_data; | |
| // Clear the input | |
| document.getElementById('roomIdInput').value = ''; | |
| // Show game area and enable chat | |
| this.gameArea.style.display = 'block'; | |
| this.noRoom.style.display = 'none'; | |
| this.chatInput.disabled = false; | |
| this.sendBtn.disabled = false; | |
| this.chatInput.placeholder = "Type a message..."; | |
| // Update display | |
| this.updateDisplay(); | |
| this.loadChatHistory(); | |
| this.gameStatus.textContent = `Joined room ${roomId}!`; | |
| } catch (error) { | |
| console.error('Failed to join room:', error); | |
| this.gameStatus.textContent = `Failed to join room ${roomId}. Check the room ID.`; | |
| } | |
| } | |
| leaveRoom() { | |
| this.currentRoomId = null; | |
| this.roomData = null; | |
| // Hide game area and disable chat | |
| this.gameArea.style.display = 'none'; | |
| this.noRoom.style.display = 'block'; | |
| this.chatInput.disabled = true; | |
| this.sendBtn.disabled = true; | |
| this.chatInput.placeholder = "Join a room first..."; | |
| // Clear display | |
| this.clearBoard(); | |
| this.gameStatus.textContent = "Create or join a room to start playing!"; | |
| this.updateRoomInfo(); | |
| // Clear chat | |
| this.chatMessages.innerHTML = ` | |
| <div class="message ai"> | |
| <div class="message-sender">System:</div> | |
| <div>Create or join a room to start chatting with Mistral AI!</div> | |
| </div> | |
| `; | |
| } | |
| async refreshRoomState() { | |
| if (!this.currentRoomId) return; | |
| try { | |
| const response = await fetch(`/rooms/${this.currentRoomId}`); | |
| if (!response.ok) { | |
| if (response.status === 404) { | |
| this.gameStatus.textContent = "Room no longer exists!"; | |
| this.leaveRoom(); | |
| return; | |
| } | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| this.roomData = data.room_data; | |
| this.updateDisplay(); | |
| } catch (error) { | |
| console.error('Failed to refresh room:', error); | |
| } | |
| } | |
| async handleCellClick(index) { | |
| if (!this.currentRoomId || !this.roomData) { | |
| this.gameStatus.textContent = "Join a room first!"; | |
| return; | |
| } | |
| if (this.roomData.game_status !== 'active' || | |
| this.roomData.board[index] !== '' || | |
| this.roomData.current_player !== 'X') { | |
| return; | |
| } | |
| this.gameStatus.textContent = "Making your move..."; | |
| try { | |
| const response = await fetch(`/rooms/${this.currentRoomId}/move`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| position: index | |
| }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| this.roomData = data.room_data; | |
| this.updateDisplay(); | |
| this.loadChatHistory(); // Reload chat to get AI's move message | |
| if (this.roomData.game_status === 'active') { | |
| this.gameStatus.textContent = "Mistral is thinking..."; | |
| setTimeout(() => { | |
| if (this.roomData.current_player === 'X') { | |
| this.gameStatus.textContent = "Your turn! Click a square."; | |
| } | |
| }, 1000); | |
| } | |
| } catch (error) { | |
| console.error('Move failed:', error); | |
| this.gameStatus.textContent = "Move failed. Try again."; | |
| } | |
| } | |
| async sendChatMessage() { | |
| if (!this.currentRoomId) { | |
| return; | |
| } | |
| const message = this.chatInput.value.trim(); | |
| if (!message) return; | |
| this.chatInput.value = ''; | |
| try { | |
| const response = await fetch(`/rooms/${this.currentRoomId}/chat`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| message: message | |
| }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| this.roomData = data.room_data; | |
| this.loadChatHistory(); | |
| } catch (error) { | |
| console.error('Chat failed:', error); | |
| this.addChatMessage("Failed to send message", 'system'); | |
| } | |
| } | |
| updateDisplay() { | |
| if (!this.roomData) return; | |
| // Update board | |
| this.roomData.board.forEach((cell, index) => { | |
| this.cells[index].textContent = cell; | |
| this.cells[index].className = 'cell'; | |
| if (cell) { | |
| this.cells[index].classList.add(cell.toLowerCase()); | |
| } | |
| }); | |
| // Update game status | |
| if (this.roomData.game_status === 'won') { | |
| const winner = this.roomData.winner === 'X' ? 'You' : 'Mistral AI'; | |
| this.gameStatus.textContent = `🎉 ${winner} won!`; | |
| } else if (this.roomData.game_status === 'draw') { | |
| this.gameStatus.textContent = "🤝 It's a draw!"; | |
| } else if (this.roomData.current_player === 'X') { | |
| this.gameStatus.textContent = "Your turn! Click a square."; | |
| } else { | |
| this.gameStatus.textContent = "Mistral's turn..."; | |
| } | |
| this.updateRoomInfo(); | |
| } | |
| updateRoomInfo() { | |
| if (!this.roomData || !this.currentRoomId) { | |
| this.roomInfo.innerHTML = ` | |
| <div>Status: No room selected</div> | |
| <div>Room ID: -</div> | |
| <div>Game Status: -</div> | |
| <div>Your Turn: -</div> | |
| `; | |
| return; | |
| } | |
| const isYourTurn = this.roomData.current_player === 'X' && this.roomData.game_status === 'active'; | |
| this.roomInfo.innerHTML = ` | |
| <div>Status: Connected</div> | |
| <div>Room ID: ${this.currentRoomId}</div> | |
| <div>Game Status: ${this.roomData.game_status}</div> | |
| <div>Your Turn: ${isYourTurn ? 'Yes' : 'No'}</div> | |
| <div>Moves: ${this.roomData.moves_count}/9</div> | |
| `; | |
| } | |
| loadChatHistory() { | |
| if (!this.roomData || !this.roomData.chat_history) return; | |
| this.chatMessages.innerHTML = ''; | |
| this.roomData.chat_history.forEach(msg => { | |
| this.addChatMessage(msg.message, msg.sender); | |
| }); | |
| } | |
| addChatMessage(message, sender) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message ${sender}`; | |
| const senderDiv = document.createElement('div'); | |
| senderDiv.className = 'message-sender'; | |
| let senderName; | |
| if (sender === 'user') senderName = 'You:'; | |
| else if (sender === 'ai') senderName = 'Mistral AI:'; | |
| else senderName = 'System:'; | |
| senderDiv.textContent = senderName; | |
| const contentDiv = document.createElement('div'); | |
| contentDiv.textContent = message; | |
| messageDiv.appendChild(senderDiv); | |
| messageDiv.appendChild(contentDiv); | |
| this.chatMessages.appendChild(messageDiv); | |
| // Scroll to bottom | |
| this.chatMessages.scrollTop = this.chatMessages.scrollHeight; | |
| } | |
| clearBoard() { | |
| this.cells.forEach(cell => { | |
| cell.textContent = ''; | |
| cell.className = 'cell'; | |
| }); | |
| } | |
| async resetGame() { | |
| if (!this.currentRoomId) return; | |
| // Create a new room instead of resetting current one | |
| await this.createNewRoom(); | |
| } | |
| } | |
| // Global functions for HTML onclick events | |
| let game; | |
| function createNewRoom() { | |
| game.createNewRoom(); | |
| } | |
| function joinRoom() { | |
| game.joinRoom(); | |
| } | |
| function leaveRoom() { | |
| game.leaveRoom(); | |
| } | |
| function sendChatMessage() { | |
| game.sendChatMessage(); | |
| } | |
| function handleEnter(event) { | |
| if (event.key === 'Enter') { | |
| sendChatMessage(); | |
| } | |
| } | |
| function resetGame() { | |
| game.resetGame(); | |
| } | |
| // Initialize game when page loads | |
| document.addEventListener('DOMContentLoaded', () => { | |
| game = new RoomTicTacToeGame(); | |
| }); |