tictoctoe-ui / room_game.js
ravithejads's picture
Upload 6 files
2bdee50 verified
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();
});