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(); | |
}); |