// Educational Tic Tac Toe Game // Game variables let currentPlayer = 'X'; let gameBoard = ['', '', '', '', '', '', '', '', '']; let gameActive = true; let scores = { X: 0, O: 0 }; let gameSettings = { questionType: 'multiplechoice', language: 'arabic', category: 'beginners', // Changed to beginners for new users timerSeconds: 5 // 5 seconds timer }; // Timer variables let timerInterval = null; let timeLeft = 0; // DOM Elements const cells = document.querySelectorAll('.cell'); const player1Element = document.getElementById('player1'); const player2Element = document.getElementById('player2'); const resetButton = document.getElementById('resetBtn'); const newGameButton = document.getElementById('newGameBtn'); const questionModal = document.getElementById('questionModal'); const questionText = document.getElementById('questionText'); const answerOptions = document.getElementById('answerOptions'); const feedback = document.getElementById('feedback'); const gameOverModal = document.getElementById('gameOverModal'); const gameResult = document.getElementById('gameResult'); const playAgainButton = document.getElementById('playAgainBtn'); const applySettingsButton = document.getElementById('applySettingsBtn'); // Timer elements const timerContainer = document.getElementById('timerContainer'); const timerBar = document.getElementById('timerBar'); const timerText = document.getElementById('timerText'); // Settings elements const questionTypeSelect = document.getElementById('questionType'); const questionLanguageSelect = document.getElementById('questionLanguage'); const questionCategorySelect = document.getElementById('questionCategory'); const questionTimerSelect = document.getElementById('questionTimer'); // Current question and selected cell let currentQuestion = null; let selectedCellIndex = null; let questionPlayer = null; // Track which player clicked the cell // Initialize the game function initGame() { // Add event listeners to cells cells.forEach(cell => { cell.addEventListener('click', () => handleCellClick(parseInt(cell.dataset.index))); }); // Add event listeners to buttons resetButton.addEventListener('click', resetGame); newGameButton.addEventListener('click', newGame); playAgainButton.addEventListener('click', newGame); applySettingsButton.addEventListener('click', applySettings); // Add event listener to language select to update categories questionLanguageSelect.addEventListener('change', updateCategoryOptions); // Initialize settings from selects questionTypeSelect.value = gameSettings.questionType; questionLanguageSelect.value = gameSettings.language; questionCategorySelect.value = gameSettings.category; questionTimerSelect.value = gameSettings.timerSeconds; // Update category options based on selected language updateCategoryOptions(); // Set up the initial game state updateGameDisplay(); // Update UI language immediately based on default settings updateUILanguage(); // Set document direction for Arabic if (gameSettings.language === 'arabic') { document.documentElement.dir = 'rtl'; } } // Update category options based on selected language function updateCategoryOptions() { const language = questionLanguageSelect.value; // Clear existing options questionCategorySelect.innerHTML = ''; if (language === 'english') { // Add English categories const mathOption = document.createElement('option'); mathOption.value = 'math'; mathOption.textContent = 'Mathematics'; questionCategorySelect.appendChild(mathOption); // Set default for English questionCategorySelect.value = 'math'; } else if (language === 'arabic') { // Add Arabic categories const islamicOption = document.createElement('option'); islamicOption.value = 'islamic'; islamicOption.textContent = 'Islamic'; questionCategorySelect.appendChild(islamicOption); const beginnersOption = document.createElement('option'); beginnersOption.value = 'beginners'; beginnersOption.textContent = 'Beginners Arabic'; questionCategorySelect.appendChild(beginnersOption); // Set default for Arabic questionCategorySelect.value = 'beginners'; } } // Handle cell click function handleCellClick(index) { // Ignore clicks if game is not active or cell is already filled if (!gameActive || gameBoard[index] !== '') return; // Store the selected cell index and current player selectedCellIndex = index; questionPlayer = currentPlayer; // Store which player clicked the cell // Get a random question based on current settings currentQuestion = getRandomQuestion( gameSettings.language, gameSettings.category, gameSettings.questionType ); // Display the question showQuestion(currentQuestion); } // Show question modal function showQuestion(question) { // Set the question text with appropriate language attribute questionText.textContent = question.question; if (gameSettings.language === 'arabic') { questionText.setAttribute('lang', 'ar'); } else { questionText.removeAttribute('lang'); } // Clear previous answer options and feedback answerOptions.innerHTML = ''; feedback.textContent = ''; feedback.className = 'feedback'; // Create answer options based on question type if (gameSettings.questionType === 'truefalse') { // True/False question const trueOption = document.createElement('div'); trueOption.className = 'answer-option'; trueOption.textContent = gameSettings.language === 'arabic' ? 'صحيح' : 'True'; trueOption.addEventListener('click', () => checkAnswer(true)); const falseOption = document.createElement('div'); falseOption.className = 'answer-option'; falseOption.textContent = gameSettings.language === 'arabic' ? 'خطأ' : 'False'; falseOption.addEventListener('click', () => checkAnswer(false)); answerOptions.appendChild(trueOption); answerOptions.appendChild(falseOption); } else { // Multiple choice question // Create a shuffled array of indices const originalAnswer = question.answer; const indices = Array.from({ length: question.options.length }, (_, i) => i); const shuffledIndices = shuffleArray([...indices]); // Create a mapping from shuffled positions to original positions const shuffleMap = {}; shuffledIndices.forEach((originalIndex, newIndex) => { shuffleMap[newIndex] = originalIndex; }); // Display options in shuffled order shuffledIndices.forEach((originalIndex, newIndex) => { const optionElement = document.createElement('div'); optionElement.className = 'answer-option'; optionElement.textContent = question.options[originalIndex]; if (gameSettings.language === 'arabic') { optionElement.setAttribute('lang', 'ar'); } // When clicked, map back to the original index for checking optionElement.addEventListener('click', () => checkAnswer(originalIndex, shuffleMap)); answerOptions.appendChild(optionElement); }); } // Show the modal questionModal.classList.add('show'); // Setup timer if enabled if (gameSettings.timerSeconds > 0) { // Show timer container timerContainer.style.display = 'block'; // Reset timer timeLeft = gameSettings.timerSeconds; updateTimerDisplay(); // Clear any existing timer if (timerInterval) { clearInterval(timerInterval); } // Start the timer timerInterval = setInterval(() => { timeLeft--; updateTimerDisplay(); if (timeLeft <= 0) { // Time's up clearInterval(timerInterval); timeOut(); } }, 1000); } else { // Hide timer if disabled timerContainer.style.display = 'none'; } } // Update the timer display function updateTimerDisplay() { // Update text timerText.textContent = `${timeLeft}s`; // Update progress bar const percentage = (timeLeft / gameSettings.timerSeconds) * 100; timerBar.style.width = `${percentage}%`; // Change color based on time left timerBar.className = 'timer-bar'; if (timeLeft <= Math.floor(gameSettings.timerSeconds / 3)) { timerBar.classList.add('danger'); } else if (timeLeft <= Math.floor(gameSettings.timerSeconds / 2)) { timerBar.classList.add('warning'); } } // Handle time out function timeOut() { // Show feedback feedback.textContent = gameSettings.language === 'arabic' ? 'انتهى الوقت!' : 'Time\'s up!'; feedback.classList.add('incorrect'); // Switch players after a delay setTimeout(() => { hideQuestionModal(); switchPlayer(); }, 1500); } // Check the answer function checkAnswer(userAnswer, shuffleMap = null) { // Clear the timer if it's running if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } // Get all answer options const options = document.querySelectorAll('.answer-option'); // Determine if the answer is correct let isCorrect = false; if (gameSettings.questionType === 'truefalse') { isCorrect = userAnswer === currentQuestion.answer; // Highlight the selected option options[userAnswer ? 0 : 1].classList.add('selected'); // Highlight the correct option options[currentQuestion.answer ? 0 : 1].classList.add('correct'); // If wrong, highlight as incorrect if (!isCorrect) { options[userAnswer ? 0 : 1].classList.add('incorrect'); } } else { isCorrect = userAnswer === currentQuestion.answer; // Find the positions in the shuffled array let selectedPosition = -1; let correctPosition = -1; if (shuffleMap) { // Find the position of the selected answer in the shuffled array for (let i = 0; i < options.length; i++) { if (shuffleMap[i] === userAnswer) { selectedPosition = i; } if (shuffleMap[i] === currentQuestion.answer) { correctPosition = i; } } } else { // If no shuffle map (shouldn't happen), use original positions selectedPosition = userAnswer; correctPosition = currentQuestion.answer; } // Highlight the selected option if (selectedPosition >= 0 && selectedPosition < options.length) { options[selectedPosition].classList.add('selected'); } // Highlight the correct option if (correctPosition >= 0 && correctPosition < options.length) { options[correctPosition].classList.add('correct'); } // If wrong, highlight as incorrect if (!isCorrect && selectedPosition >= 0 && selectedPosition < options.length) { options[selectedPosition].classList.add('incorrect'); } } // Show feedback feedback.textContent = isCorrect ? (gameSettings.language === 'arabic' ? 'إجابة صحيحة!' : 'Correct!') : (gameSettings.language === 'arabic' ? 'إجابة خاطئة!' : 'Incorrect!'); feedback.classList.add(isCorrect ? 'correct' : 'incorrect'); // Show explanation if available if (currentQuestion.explanation) { setTimeout(() => { feedback.textContent += ' ' + currentQuestion.explanation; }, 500); } // If correct, make the move after a short delay if (isCorrect) { setTimeout(() => { // Make sure we're using the player who clicked the cell if (currentPlayer !== questionPlayer) { // If the player has changed, switch back to the original player currentPlayer = questionPlayer; // Update the active player display player1Element.classList.toggle('active', currentPlayer === 'X'); player2Element.classList.toggle('active', currentPlayer === 'O'); } makeMove(selectedCellIndex); hideQuestionModal(); }, 1500); } else { // If incorrect, close the modal after a delay and switch players setTimeout(() => { hideQuestionModal(); // Only switch if the current player is still the one who clicked if (currentPlayer === questionPlayer) { switchPlayer(); } // Reset the question player questionPlayer = null; }, 2000); } } // Hide the question modal function hideQuestionModal() { // Clear the timer if it's running if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } questionModal.classList.remove('show'); } // Make a move on the board function makeMove(index) { // Update the game board gameBoard[index] = currentPlayer; // Update the cell display cells[index].textContent = currentPlayer; cells[index].classList.add(currentPlayer.toLowerCase()); // Check for win or draw if (checkWin()) { endGame(false); } else if (checkDraw()) { endGame(true); } else { // Switch players switchPlayer(); } } // Switch the current player function switchPlayer() { currentPlayer = currentPlayer === 'X' ? 'O' : 'X'; // Update the active player display player1Element.classList.toggle('active'); player2Element.classList.toggle('active'); } // Check if the current player has won function checkWin() { 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 (gameBoard[a] && gameBoard[a] === gameBoard[b] && gameBoard[a] === gameBoard[c]) { // Highlight the winning cells cells[a].classList.add('highlight'); cells[b].classList.add('highlight'); cells[c].classList.add('highlight'); return true; } } return false; } // Check if the game is a draw function checkDraw() { return gameBoard.every(cell => cell !== ''); } // End the game function endGame(isDraw) { gameActive = false; if (!isDraw) { // Update scores scores[currentPlayer]++; document.querySelector(`#player${currentPlayer === 'X' ? '1' : '2'} .player-score`).textContent = scores[currentPlayer]; // Show game over message gameResult.textContent = gameSettings.language === 'arabic' ? `اللاعب ${currentPlayer} فاز!` : `Player ${currentPlayer} wins!`; } else { // Show draw message gameResult.textContent = gameSettings.language === 'arabic' ? 'تعادل!' : 'It\'s a draw!'; } // Show game over modal setTimeout(() => { gameOverModal.classList.add('show'); }, 1000); } // Reset the current game function resetGame() { // Clear the board gameBoard = ['', '', '', '', '', '', '', '', '']; gameActive = true; currentPlayer = 'X'; // Reset the UI cells.forEach(cell => { cell.textContent = ''; cell.className = 'cell'; }); player1Element.classList.add('active'); player2Element.classList.remove('active'); // Clear any active timers if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } // Hide modals questionModal.classList.remove('show'); gameOverModal.classList.remove('show'); } // Start a new game (reset game and scores) function newGame() { // Reset scores scores = { X: 0, O: 0 }; document.querySelector('#player1 .player-score').textContent = '0'; document.querySelector('#player2 .player-score').textContent = '0'; // Reset the game resetGame(); } // Apply new settings function applySettings() { gameSettings.questionType = questionTypeSelect.value; gameSettings.language = questionLanguageSelect.value; gameSettings.category = questionCategorySelect.value; gameSettings.timerSeconds = parseInt(questionTimerSelect.value); // Validate language-category combination // Make sure the selected category exists for the selected language if (gameSettings.language === 'english' && gameSettings.category !== 'math') { // For English, only math category is available gameSettings.category = 'math'; questionCategorySelect.value = 'math'; } else if (gameSettings.language === 'arabic' && (gameSettings.category !== 'islamic' && gameSettings.category !== 'beginners')) { // For Arabic, only islamic and beginners categories are available gameSettings.category = 'beginners'; questionCategorySelect.value = 'beginners'; } // Update UI language if needed updateUILanguage(); // Start a new game with new settings newGame(); } // Update UI language based on selected language function updateUILanguage() { const isArabic = gameSettings.language === 'arabic'; // Update document direction document.documentElement.dir = isArabic ? 'rtl' : 'ltr'; // Update button texts resetButton.textContent = isArabic ? 'إعادة اللعبة' : 'Reset Game'; newGameButton.textContent = isArabic ? 'لعبة جديدة' : 'New Game'; playAgainButton.textContent = isArabic ? 'العب مرة أخرى' : 'Play Again'; applySettingsButton.textContent = isArabic ? 'تطبيق الإعدادات' : 'Apply Settings'; // Update headings document.querySelector('h1').textContent = isArabic ? 'لعبة تيك تاك تو التعليمية' : 'Educational Tic Tac Toe'; document.querySelector('.settings h3').textContent = isArabic ? 'إعدادات اللعبة' : 'Game Settings'; document.querySelector('.used-words-container h3').textContent = isArabic ? 'الكلمات المستخدمة:' : 'Selected Words:'; // Update player names document.querySelector('#player1 .player-name').textContent = isArabic ? 'اللاعب 1' : 'Player 1'; document.querySelector('#player2 .player-name').textContent = isArabic ? 'اللاعب 2' : 'Player 2'; // Update settings labels const labels = document.querySelectorAll('.setting-group label'); if (isArabic) { labels[0].textContent = 'نوع السؤال:'; labels[1].textContent = 'اللغة:'; labels[2].textContent = 'الفئة:'; labels[3].textContent = 'الوقت المحدد (ثواني):'; } else { labels[0].textContent = 'Question Type:'; labels[1].textContent = 'Language:'; labels[2].textContent = 'Category:'; labels[3].textContent = 'Time Limit (seconds):'; } // Update modal texts document.querySelector('#questionModal h2').textContent = isArabic ? 'أجب على السؤال لإجراء حركتك' : 'Answer the question to make your move'; } // Update the game display function updateGameDisplay() { // Update UI language updateUILanguage(); // Update player scores document.querySelector('#player1 .player-score').textContent = scores.X; document.querySelector('#player2 .player-score').textContent = scores.O; // Update active player player1Element.classList.toggle('active', currentPlayer === 'X'); player2Element.classList.toggle('active', currentPlayer === 'O'); } // Utility function to shuffle an array (Fisher-Yates algorithm) function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } // Initialize the game when the page loads window.addEventListener('load', initGame);