Pp's picture
Upload 3 files
28a75da verified
document.addEventListener('DOMContentLoaded', () => {
const scoreDisplay = document.getElementById('score');
const numeratorDisplay = document.querySelector('#fraction-order .numerator');
const denominatorDisplay = document.querySelector('#fraction-order .denominator');
const feedbackArea = document.getElementById('feedback-area');
const pizzaChoicesContainer = document.getElementById('pizza-choices');
const PIZZA_SIZE = 140; // Diameter of the pizza canvas
const OPTIONS_COUNT = 3; // Number of pizza choices to show (1 correct, 2 incorrect)
// --- Define Possible Fractions ---
// Start simple, can add more complex ones later
const possibleFractions = [
// Denominator 2
{ num: 1, den: 2 },
// Denominator 3
{ num: 1, den: 3 }, { num: 2, den: 3 },
// Denominator 4
{ num: 1, den: 4 }, { num: 2, den: 4 }, { num: 3, den: 4 },
// Denominator 6
{ num: 1, den: 6 }, { num: 2, den: 6 }, { num: 3, den: 6 }, { num: 4, den: 6 }, { num: 5, den: 6 },
// Denominator 8
{ num: 1, den: 8 }, { num: 2, den: 8 }, { num: 3, den: 8 }, { num: 4, den: 8 },
{ num: 5, den: 8 }, { num: 6, den: 8 }, { num: 7, den: 8 },
];
// --- Game State ---
let score = 0;
let currentOrder = null; // Will hold {num, den} of the target fraction
let waitingForNext = false; // Flag to prevent clicks during feedback/delay
// --- Utility: Get Random Int ---
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
// --- Utility: Shuffle Array ---
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = getRandomInt(i + 1);
[array[i], array[j]] = [array[j], array[i]]; // Swap elements
}
}
// --- Canvas Drawing Function ---
function drawPizza(canvas, num, den) {
const ctx = canvas.getContext('2d');
const radius = canvas.width / 2;
const centerX = radius;
const centerY = radius;
const sliceAngle = (Math.PI * 2) / den;
// Colors
const crustColor = '#f4a460'; // Sandy brown
const sauceColor = '#ff6347'; // Tomato red
const cheeseColor = '#fffacd'; // Lemon chiffon (looks like cheese)
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw base crust slightly larger
ctx.fillStyle = crustColor;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.fill();
// Draw cheese base smaller than crust
ctx.fillStyle = cheeseColor;
ctx.beginPath();
ctx.arc(centerX, centerY, radius * 0.95, 0, Math.PI * 2);
ctx.fill();
// Draw highlighted slices (sauce/toppings)
ctx.fillStyle = sauceColor;
for (let i = 0; i < num; i++) {
const startAngle = i * sliceAngle - Math.PI / 2; // Start from top
const endAngle = (i + 1) * sliceAngle - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, radius * 0.95, startAngle, endAngle); // Fill up to cheese edge
ctx.closePath();
ctx.fill();
}
// Draw slice lines on top
ctx.strokeStyle = crustColor; // Darker lines for contrast
ctx.lineWidth = 2;
for (let i = 0; i < den; i++) {
const angle = i * sliceAngle - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(centerX + radius * 0.95 * Math.cos(angle), centerY + radius * 0.95 * Math.sin(angle));
ctx.stroke();
}
}
// --- Setup a New Round ---
function setupNewRound() {
waitingForNext = false;
pizzaChoicesContainer.innerHTML = ''; // Clear previous pizzas
feedbackArea.textContent = "Click the pizza that matches the order!";
feedbackArea.className = ''; // Reset feedback style
// 1. Choose a target fraction
currentOrder = possibleFractions[getRandomInt(possibleFractions.length)];
numeratorDisplay.textContent = currentOrder.num;
denominatorDisplay.textContent = currentOrder.den;
// 2. Create options array, starting with the correct one
const options = [{ num: currentOrder.num, den: currentOrder.den }];
// 3. Generate incorrect options
while (options.length < OPTIONS_COUNT) {
const randomFraction = possibleFractions[getRandomInt(possibleFractions.length)];
// Make sure it's not the same as the target OR already in the options
const isDuplicate = options.some(opt => opt.num === randomFraction.num && opt.den === randomFraction.den);
if (!isDuplicate) {
options.push(randomFraction);
}
// Basic protection against infinite loop if somehow options are very limited
if(options.length < OPTIONS_COUNT && possibleFractions.length <= options.length){
console.warn("Not enough unique fractions to generate distractors. Reusing.");
// Add a non-duplicate if possible, otherwise just add another random one
let foundNew = false;
for(let frac of possibleFractions){
if(!options.some(opt => opt.num === frac.num && opt.den === frac.den)){
options.push(frac);
foundNew = true;
break;
}
}
if(!foundNew && options.length < OPTIONS_COUNT) options.push(randomFraction); // Add duplicate if absolutely needed
}
}
// 4. Shuffle options
shuffleArray(options);
// 5. Create and display pizza elements
options.forEach(fraction => {
const optionDiv = document.createElement('div');
optionDiv.classList.add('pizza-option');
optionDiv.dataset.num = fraction.num;
optionDiv.dataset.den = fraction.den;
const canvas = document.createElement('canvas');
canvas.classList.add('pizza-canvas');
canvas.width = PIZZA_SIZE;
canvas.height = PIZZA_SIZE;
optionDiv.appendChild(canvas);
pizzaChoicesContainer.appendChild(optionDiv);
drawPizza(canvas, fraction.num, fraction.den);
optionDiv.addEventListener('click', handleChoiceClick);
});
console.log("New round setup. Target:", currentOrder);
}
// --- Handle Pizza Choice Click ---
function handleChoiceClick(event) {
if (waitingForNext) return; // Don't process clicks during delay
const clickedPizza = event.currentTarget; // The div element
const clickedNum = parseInt(clickedPizza.dataset.num);
const clickedDen = parseInt(clickedPizza.dataset.den);
waitingForNext = true; // Prevent further clicks until next round
// Remove event listeners from all options to prevent multiple clicks
document.querySelectorAll('.pizza-option').forEach(opt => {
// Clone and replace to remove listeners easily, or store listeners and remove them
const clone = opt.cloneNode(true);
opt.parentNode.replaceChild(clone, opt);
});
// Check if correct
if (clickedNum === currentOrder.num && clickedDen === currentOrder.den) {
// Correct!
score++;
scoreDisplay.textContent = score;
feedbackArea.textContent = `Yummy! That's ${currentOrder.num}/${currentOrder.den}! πŸŽ‰`;
feedbackArea.className = 'correct-feedback';
clickedPizza.classList.add('correct-choice'); // Add animation class to the actual clicked element
// Find the *new* clone in the DOM to add the class (since we replaced it)
const newClickedElement = pizzaChoicesContainer.querySelector(`.pizza-option[data-num="${clickedNum}"][data-den="${clickedDen}"]`);
if (newClickedElement) newClickedElement.classList.add('correct-choice');
} else {
// Incorrect!
feedbackArea.textContent = `Oops! That shows ${clickedNum}/${clickedDen}. Try the next one! πŸ€”`;
feedbackArea.className = 'incorrect-feedback';
// Highlight the incorrect choice and the correct one (optional)
const newClickedElement = pizzaChoicesContainer.querySelector(`.pizza-option[data-num="${clickedNum}"][data-den="${clickedDen}"]`);
if (newClickedElement) newClickedElement.classList.add('incorrect-choice');
// Highlight the correct answer after a small delay
setTimeout(() => {
const correctElement = pizzaChoicesContainer.querySelector(`.pizza-option[data-num="${currentOrder.num}"][data-den="${currentOrder.den}"]`);
if (correctElement) correctElement.classList.add('correct-choice'); // Use correct style to show answer
}, 300);
}
// Load next round after a delay
setTimeout(setupNewRound, 2000); // Wait 2 seconds before next round
}
// --- Initial Game Start ---
setupNewRound();
}); // End DOMContentLoaded