tictactoe / game.js
ravithejads's picture
Upload 8 files
7bfb4cb verified
raw
history blame
9.11 kB
class TicTacToeGame {
constructor() {
this.board = Array(9).fill('');
this.currentPlayer = 'X'; // Human is X, AI is O
this.gameActive = true;
this.cells = document.querySelectorAll('.cell');
this.gameStatus = document.getElementById('gameStatus');
this.initGame();
}
initGame() {
this.cells.forEach((cell, index) => {
cell.addEventListener('click', () => this.handleCellClick(index));
});
}
async handleCellClick(index) {
if (!this.gameActive || this.board[index] !== '' || this.currentPlayer !== 'X') {
return;
}
// Make human move
this.makeMove(index, 'X');
// Check for win/draw
if (this.checkGameEnd()) {
return;
}
// AI's turn
this.gameStatus.textContent = "Mistral is thinking...";
await this.makeAIMove();
}
makeMove(index, player) {
this.board[index] = player;
this.cells[index].textContent = player;
this.cells[index].classList.add(player.toLowerCase());
}
async makeAIMove() {
try {
const aiMove = await this.getAIMoveAndChat();
console.log('AI Move received:', aiMove);
console.log('Current board:', this.board);
console.log('Move valid?', aiMove.move >= 0 && aiMove.move < 9);
console.log('Square empty?', this.board[aiMove.move] === '');
if (aiMove && typeof aiMove.move === 'number' && aiMove.move >= 0 && aiMove.move < 9 && this.board[aiMove.move] === '') {
this.makeMove(aiMove.move, 'O');
// Add AI's chat message
if (aiMove.message) {
this.addChatMessage(aiMove.message, 'ai');
}
if (!this.checkGameEnd()) {
this.gameStatus.textContent = "Your turn! Click a square.";
}
} else {
// Fallback to random move
console.warn('AI move invalid, using fallback. Move:', aiMove.move, 'Board:', this.board);
const emptySpots = this.board.map((cell, index) => cell === '' ? index : null).filter(val => val !== null);
if (emptySpots.length > 0) {
const randomMove = emptySpots[Math.floor(Math.random() * emptySpots.length)];
this.makeMove(randomMove, 'O');
this.addChatMessage("Technical hiccup! But I'm still playing! ๐Ÿค–", 'ai');
if (!this.checkGameEnd()) {
this.gameStatus.textContent = "Your turn! Click a square.";
}
} else {
this.gameStatus.textContent = "Game error - no valid moves!";
}
}
} catch (error) {
console.error('AI move failed:', error);
// Fallback to random move instead of error
const emptySpots = this.board.map((cell, index) => cell === '' ? index : null).filter(val => val !== null);
if (emptySpots.length > 0) {
const randomMove = emptySpots[Math.floor(Math.random() * emptySpots.length)];
this.makeMove(randomMove, 'O');
this.addChatMessage("Connection issues, but I'm improvising! ๐Ÿ˜…", 'ai');
if (!this.checkGameEnd()) {
this.gameStatus.textContent = "Your turn! Click a square.";
}
}
}
}
async getAIMoveAndChat() {
const response = await fetch("/ai-move", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
board: this.board
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
}
async callMistralAPI(messages) {
const response = await fetch("/mistral-proxy", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "mistral-large-latest",
messages: messages,
temperature: 0.7,
response_format: { type: "json_object" }
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.choices[0].message.content;
}
boardToString() {
let result = "";
for (let i = 0; i < 9; i += 3) {
result += `${this.board[i] || ' '} | ${this.board[i+1] || ' '} | ${this.board[i+2] || ' '}\n`;
if (i < 6) result += "---------\n";
}
return result;
}
checkGameEnd() {
const winner = this.checkWinner();
if (winner) {
this.gameActive = false;
if (winner === 'X') {
this.gameStatus.textContent = "๐ŸŽ‰ You won! Impressive!";
this.addChatMessage("Wow! You actually beat me! ๐Ÿ˜ฑ Well played, human! ๐Ÿ†", 'ai');
} else {
this.gameStatus.textContent = "๐Ÿ’€ Mistral AI wins!";
this.addChatMessage("Victory is mine! ๐Ÿ˜ˆ Better luck next time! ๐Ÿค–", 'ai');
}
return true;
}
if (this.board.every(cell => cell !== '')) {
this.gameActive = false;
this.gameStatus.textContent = "๐Ÿค It's a draw!";
this.addChatMessage("A respectable draw! You're better than I expected! ๐Ÿค", 'ai');
return true;
}
return false;
}
checkWinner() {
const winPatterns = [
[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
];
for (const pattern of winPatterns) {
const [a, b, c] = pattern;
if (this.board[a] && this.board[a] === this.board[b] && this.board[a] === this.board[c]) {
return this.board[a];
}
}
return null;
}
addChatMessage(message, sender) {
const chatMessages = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
const senderDiv = document.createElement('div');
senderDiv.className = 'message-sender';
senderDiv.textContent = sender === 'user' ? 'You:' : 'Mistral AI:';
const contentDiv = document.createElement('div');
contentDiv.textContent = message;
messageDiv.appendChild(senderDiv);
messageDiv.appendChild(contentDiv);
chatMessages.appendChild(messageDiv);
// Scroll to bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
}
async sendChatMessage(message) {
if (!message.trim()) return;
// Add user message
this.addChatMessage(message, 'user');
try {
const aiResponse = await this.getAIChatResponse(message);
this.addChatMessage(aiResponse, 'ai');
} catch (error) {
console.error('Chat failed:', error);
this.addChatMessage("Sorry, I'm having trouble responding right now! ๐Ÿค–", 'ai');
}
}
async getAIChatResponse(userMessage) {
const response = await fetch("/ai-chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message: userMessage,
board: this.board
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.message;
}
}
// Initialize game when page loads
let game;
document.addEventListener('DOMContentLoaded', () => {
game = new TicTacToeGame();
});
// Chat functions
function sendChatMessage() {
const chatInput = document.getElementById('chatInput');
const message = chatInput.value.trim();
if (message) {
game.sendChatMessage(message);
chatInput.value = '';
}
}
function handleEnter(event) {
if (event.key === 'Enter') {
sendChatMessage();
}
}
// Reset game
function resetGame() {
game = new TicTacToeGame();
// Clear board visually
game.cells.forEach(cell => {
cell.textContent = '';
cell.classList.remove('x', 'o');
});
game.gameStatus.textContent = "Your turn! Click a square to play X";
game.addChatMessage("New game! Let's see if you can do better this time! ๐Ÿ˜", 'ai');
}