// 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 = `
${index + 1}. ${task.title}
`;
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 = 'Помилка';
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 = '✓ Правильно!';
} else {
outputStatus.innerHTML = 'Не зовсім правильно';
}
} else {
outputStatus.innerHTML = 'Виконано';
}
} catch (error) {
output.textContent = `Помилка: ${error.message}`;
outputStatus.innerHTML = 'Помилка виконання';
}
}
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();
});