File size: 9,344 Bytes
28a75da |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
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 |