Spaces:
Running
Running
| // Python Code Tester Application | |
| class PythonCodeTester { | |
| constructor() { | |
| this.tasks = [ | |
| { | |
| id: 1, | |
| title: "Створення змінних", | |
| description: "Створіть змінні для збереження вашого імені (рядок), віку (ціле число) та зросту (дійсне число). Виведіть їх на екран.", | |
| hint: "Використовуйте змінні name, age, height та функцію print()", | |
| solution: "name = \"Іван\"\nage = 20\nheight = 1.75\nprint(\"Ім'я:\", name)\nprint(\"Вік:\", age)\nprint(\"Зріст:\", height)", | |
| expected_output: "Ім'я: Іван\nВік: 20\nЗріст: 1.75" | |
| }, | |
| { | |
| id: 2, | |
| title: "Арифметичні операції", | |
| description: "Обчисліть площу прямокутника зі сторонами 15 та 25. Виведіть результат.", | |
| hint: "Площа = довжина × ширина", | |
| solution: "length = 15\nwidth = 25\narea = length * width\nprint(\"Площа прямокутника:\", area)", | |
| expected_output: "Площа прямокутника: 375" | |
| }, | |
| { | |
| id: 3, | |
| title: "Перетворення типів", | |
| description: "Перетворіть рядок '42' у ціле число, додайте до нього 8 та виведіть результат.", | |
| hint: "Використовуйте функцію int() для перетворення", | |
| solution: "number_str = '42'\nnumber = int(number_str)\nresult = number + 8\nprint(\"Результат:\", result)", | |
| expected_output: "Результат: 50" | |
| }, | |
| { | |
| id: 4, | |
| title: "Робота з булевими значеннями", | |
| description: "Створіть дві булеві змінні: is_student=True та has_id=False. Виведіть результат логічного AND та OR для цих змінних.", | |
| hint: "Використовуйте оператори and та or", | |
| solution: "is_student = True\nhas_id = False\nprint(\"AND результат:\", is_student and has_id)\nprint(\"OR результат:\", is_student or has_id)", | |
| expected_output: "AND результат: False\nOR результат: True" | |
| }, | |
| { | |
| id: 5, | |
| title: "Простий калькулятор", | |
| description: "Створіть програму, яка додає два числа 15.5 та 24.3 та виводить результат з описом.", | |
| hint: "Використовуйте оператор + та форматування виводу", | |
| solution: "num1 = 15.5\nnum2 = 24.3\nsum_result = num1 + num2\nprint(f\"Сума {num1} + {num2} = {sum_result}\")", | |
| expected_output: "Сума 15.5 + 24.3 = 39.8" | |
| } | |
| ]; | |
| this.currentTask = null; | |
| this.initializeApp(); | |
| } | |
| initializeApp() { | |
| this.renderTasks(); | |
| this.setupEventListeners(); | |
| } | |
| renderTasks() { | |
| const taskList = document.getElementById('taskList'); | |
| taskList.innerHTML = ''; | |
| this.tasks.forEach((task, index) => { | |
| const taskItem = document.createElement('div'); | |
| taskItem.className = 'task-item'; | |
| taskItem.dataset.taskId = task.id; | |
| taskItem.innerHTML = ` | |
| <h4>${index + 1}. ${task.title}</h4> | |
| `; | |
| taskItem.addEventListener('click', () => this.selectTask(task)); | |
| taskList.appendChild(taskItem); | |
| }); | |
| } | |
| selectTask(task) { | |
| this.currentTask = task; | |
| // Update active task styling | |
| document.querySelectorAll('.task-item').forEach(item => { | |
| item.classList.remove('active'); | |
| }); | |
| document.querySelector(`[data-task-id="${task.id}"]`).classList.add('active'); | |
| // Update task display | |
| document.getElementById('currentTaskTitle').textContent = task.title; | |
| document.getElementById('taskDescription').textContent = task.description; | |
| // Show task action buttons | |
| document.getElementById('hintBtn').style.display = 'inline-flex'; | |
| document.getElementById('solutionBtn').style.display = 'inline-flex'; | |
| // Clear previous output | |
| document.getElementById('output').textContent = 'Натисніть "Запустити" для виконання коду...'; | |
| document.getElementById('outputStatus').textContent = ''; | |
| } | |
| setupEventListeners() { | |
| // Run button | |
| document.getElementById('runBtn').addEventListener('click', () => this.runCode()); | |
| // Hint button | |
| document.getElementById('hintBtn').addEventListener('click', () => this.showHint()); | |
| // Solution button | |
| document.getElementById('solutionBtn').addEventListener('click', () => this.showSolution()); | |
| // Modal close buttons | |
| document.getElementById('closeHintModal').addEventListener('click', () => this.closeModal('hintModal')); | |
| document.getElementById('closeSolutionModal').addEventListener('click', () => this.closeModal('solutionModal')); | |
| // Close modal on backdrop click | |
| document.getElementById('hintModal').addEventListener('click', (e) => { | |
| if (e.target.id === 'hintModal') this.closeModal('hintModal'); | |
| }); | |
| document.getElementById('solutionModal').addEventListener('click', (e) => { | |
| if (e.target.id === 'solutionModal') this.closeModal('solutionModal'); | |
| }); | |
| // Code editor enhancements | |
| const codeEditor = document.getElementById('codeEditor'); | |
| codeEditor.addEventListener('keydown', (e) => this.handleEditorKeydown(e)); | |
| // Example code click handlers | |
| document.querySelectorAll('.example-section code').forEach(code => { | |
| code.addEventListener('click', () => { | |
| const codeText = code.textContent; | |
| const editor = document.getElementById('codeEditor'); | |
| const currentCode = editor.value; | |
| if (currentCode.trim() === '' || currentCode === codeEditor.placeholder) { | |
| editor.value = codeText; | |
| } else { | |
| editor.value = currentCode + '\n' + codeText; | |
| } | |
| editor.focus(); | |
| }); | |
| }); | |
| } | |
| handleEditorKeydown(e) { | |
| // Handle Tab key for indentation | |
| if (e.key === 'Tab') { | |
| e.preventDefault(); | |
| const editor = e.target; | |
| const start = editor.selectionStart; | |
| const end = editor.selectionEnd; | |
| editor.value = editor.value.substring(0, start) + ' ' + editor.value.substring(end); | |
| editor.selectionStart = editor.selectionEnd = start + 4; | |
| } | |
| } | |
| runCode() { | |
| const code = document.getElementById('codeEditor').value.trim(); | |
| const output = document.getElementById('output'); | |
| const outputStatus = document.getElementById('outputStatus'); | |
| if (!code) { | |
| output.textContent = 'Помилка: Введіть код для виконання'; | |
| outputStatus.innerHTML = '<span class="status-error">Помилка</span>'; | |
| return; | |
| } | |
| try { | |
| const result = this.simulatePythonExecution(code); | |
| output.textContent = result.output; | |
| if (this.currentTask) { | |
| const isCorrect = this.checkSolution(result.output, this.currentTask.expected_output); | |
| if (isCorrect) { | |
| outputStatus.innerHTML = '<span class="status-success">✓ Правильно!</span>'; | |
| } else { | |
| outputStatus.innerHTML = '<span class="status-error">Не зовсім правильно</span>'; | |
| } | |
| } else { | |
| outputStatus.innerHTML = '<span class="status-success">Виконано</span>'; | |
| } | |
| } catch (error) { | |
| output.textContent = `Помилка: ${error.message}`; | |
| outputStatus.innerHTML = '<span class="status-error">Помилка виконання</span>'; | |
| } | |
| } | |
| simulatePythonExecution(code) { | |
| // Simple Python code simulator | |
| let output = ''; | |
| let variables = {}; | |
| const lines = code.split('\n').filter(line => line.trim() && !line.trim().startsWith('#')); | |
| for (let line of lines) { | |
| line = line.trim(); | |
| try { | |
| if (line.includes('print(')) { | |
| const result = this.executePrintStatement(line, variables); | |
| output += result + '\n'; | |
| } else if (line.includes('=') && !line.includes('==')) { | |
| this.executeAssignment(line, variables); | |
| } | |
| } catch (error) { | |
| throw new Error(`Помилка в рядку "${line}": ${error.message}`); | |
| } | |
| } | |
| return { output: output.trim() }; | |
| } | |
| executeAssignment(line, variables) { | |
| const parts = line.split('='); | |
| if (parts.length !== 2) return; | |
| const varName = parts[0].trim(); | |
| const value = parts[1].trim(); | |
| variables[varName] = this.evaluateExpression(value, variables); | |
| } | |
| executePrintStatement(line, variables) { | |
| const match = line.match(/print\((.*)\)/); | |
| if (!match) return ''; | |
| const args = this.parseArguments(match[1]); | |
| const values = args.map(arg => this.evaluateExpression(arg, variables)); | |
| return values.join(' '); | |
| } | |
| parseArguments(argsString) { | |
| const args = []; | |
| let current = ''; | |
| let inString = false; | |
| let stringChar = ''; | |
| let depth = 0; | |
| for (let i = 0; i < argsString.length; i++) { | |
| const char = argsString[i]; | |
| if ((char === '"' || char === "'") && !inString) { | |
| inString = true; | |
| stringChar = char; | |
| current += char; | |
| } else if (char === stringChar && inString) { | |
| inString = false; | |
| current += char; | |
| } else if (char === '(' && !inString) { | |
| depth++; | |
| current += char; | |
| } else if (char === ')' && !inString) { | |
| depth--; | |
| current += char; | |
| } else if (char === ',' && !inString && depth === 0) { | |
| args.push(current.trim()); | |
| current = ''; | |
| } else { | |
| current += char; | |
| } | |
| } | |
| if (current.trim()) { | |
| args.push(current.trim()); | |
| } | |
| return args; | |
| } | |
| evaluateExpression(expr, variables) { | |
| expr = expr.trim(); | |
| // String literals | |
| if ((expr.startsWith('"') && expr.endsWith('"')) || | |
| (expr.startsWith("'") && expr.endsWith("'"))) { | |
| return expr.slice(1, -1); | |
| } | |
| // f-strings (basic support) | |
| if (expr.startsWith('f"') || expr.startsWith("f'")) { | |
| let result = expr.slice(2, -1); | |
| Object.keys(variables).forEach(varName => { | |
| const regex = new RegExp(`{${varName}}`, 'g'); | |
| result = result.replace(regex, variables[varName]); | |
| }); | |
| return result; | |
| } | |
| // Numbers | |
| if (!isNaN(expr)) { | |
| return expr.includes('.') ? parseFloat(expr) : parseInt(expr); | |
| } | |
| // Boolean values | |
| if (expr === 'True') return true; | |
| if (expr === 'False') return false; | |
| // Variables | |
| if (variables.hasOwnProperty(expr)) { | |
| return variables[expr]; | |
| } | |
| // Simple arithmetic expressions | |
| if (expr.includes('+') || expr.includes('-') || expr.includes('*') || expr.includes('/')) { | |
| return this.evaluateArithmetic(expr, variables); | |
| } | |
| // Boolean expressions | |
| if (expr.includes(' and ') || expr.includes(' or ')) { | |
| return this.evaluateBoolean(expr, variables); | |
| } | |
| // Type casting functions | |
| if (expr.startsWith('int(')) { | |
| const inner = expr.slice(4, -1); | |
| const value = this.evaluateExpression(inner, variables); | |
| return parseInt(value); | |
| } | |
| if (expr.startsWith('str(')) { | |
| const inner = expr.slice(4, -1); | |
| const value = this.evaluateExpression(inner, variables); | |
| return String(value); | |
| } | |
| if (expr.startsWith('float(')) { | |
| const inner = expr.slice(6, -1); | |
| const value = this.evaluateExpression(inner, variables); | |
| return parseFloat(value); | |
| } | |
| return expr; | |
| } | |
| evaluateArithmetic(expr, variables) { | |
| // Simple arithmetic evaluation | |
| let result = expr; | |
| // Replace variables with their values | |
| Object.keys(variables).forEach(varName => { | |
| const regex = new RegExp(`\\b${varName}\\b`, 'g'); | |
| result = result.replace(regex, variables[varName]); | |
| }); | |
| try { | |
| return eval(result); | |
| } catch { | |
| return expr; | |
| } | |
| } | |
| evaluateBoolean(expr, variables) { | |
| let result = expr; | |
| // Replace variables with their values | |
| Object.keys(variables).forEach(varName => { | |
| const regex = new RegExp(`\\b${varName}\\b`, 'g'); | |
| result = result.replace(regex, variables[varName]); | |
| }); | |
| // Replace Python boolean operators with JavaScript ones | |
| result = result.replace(/ and /g, ' && '); | |
| result = result.replace(/ or /g, ' || '); | |
| result = result.replace(/True/g, 'true'); | |
| result = result.replace(/False/g, 'false'); | |
| try { | |
| return eval(result); | |
| } catch { | |
| return expr; | |
| } | |
| } | |
| checkSolution(actualOutput, expectedOutput) { | |
| const normalize = (str) => str.replace(/\s+/g, ' ').trim().toLowerCase(); | |
| return normalize(actualOutput) === normalize(expectedOutput); | |
| } | |
| showHint() { | |
| if (!this.currentTask) return; | |
| document.getElementById('hintText').textContent = this.currentTask.hint; | |
| document.getElementById('hintModal').classList.remove('hidden'); | |
| } | |
| showSolution() { | |
| if (!this.currentTask) return; | |
| document.getElementById('solutionCode').textContent = this.currentTask.solution; | |
| document.getElementById('solutionModal').classList.remove('hidden'); | |
| } | |
| closeModal(modalId) { | |
| document.getElementById(modalId).classList.add('hidden'); | |
| } | |
| } | |
| // Initialize the application when the page loads | |
| document.addEventListener('DOMContentLoaded', () => { | |
| new PythonCodeTester(); | |
| }); |