Initial commit
This commit is contained in:
505
skills/math-teacher/references/algebra.md
Normal file
505
skills/math-teacher/references/algebra.md
Normal file
@@ -0,0 +1,505 @@
|
||||
# Algebra Reference
|
||||
|
||||
Algebraic concepts and interactive visualizations for middle school to high school.
|
||||
|
||||
## Variables & Expressions
|
||||
|
||||
### Visual Variables
|
||||
```javascript
|
||||
// Mystery box representation
|
||||
function drawVariable(name, value, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Draw box
|
||||
ctx.fillStyle = '#667eea';
|
||||
ctx.fillRect(50, 50, 100, 100);
|
||||
ctx.strokeStyle = '#333';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.strokeRect(50, 50, 100, 100);
|
||||
|
||||
// Variable name
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.font = 'bold 40px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText(name, 100, 110);
|
||||
|
||||
// Value (revealed)
|
||||
if (value !== undefined) {
|
||||
ctx.fillStyle = '#FFD700';
|
||||
ctx.font = 'bold 30px Arial';
|
||||
ctx.fillText(`= ${value}`, 100, 170);
|
||||
} else {
|
||||
ctx.fillStyle = '#FFF';
|
||||
ctx.font = '20px Arial';
|
||||
ctx.fillText('?', 100, 170);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Expression Evaluator
|
||||
```javascript
|
||||
function evaluateExpression(expr, variables) {
|
||||
// Replace variables with values
|
||||
let result = expr;
|
||||
for (const [name, value] of Object.entries(variables)) {
|
||||
result = result.replace(new RegExp(name, 'g'), value);
|
||||
}
|
||||
|
||||
try {
|
||||
return {
|
||||
expression: expr,
|
||||
substituted: result,
|
||||
result: eval(result),
|
||||
steps: showSteps(expr, variables)
|
||||
};
|
||||
} catch (e) {
|
||||
return { error: 'Invalid expression' };
|
||||
}
|
||||
}
|
||||
|
||||
function showSteps(expr, variables) {
|
||||
const steps = [];
|
||||
steps.push(`Original: ${expr}`);
|
||||
|
||||
let current = expr;
|
||||
for (const [name, value] of Object.entries(variables)) {
|
||||
current = current.replace(new RegExp(name, 'g'), value);
|
||||
steps.push(`Substitute ${name} = ${value}: ${current}`);
|
||||
}
|
||||
|
||||
steps.push(`Evaluate: ${eval(current)}`);
|
||||
return steps;
|
||||
}
|
||||
```
|
||||
|
||||
## Linear Equations
|
||||
|
||||
### Equation Solver
|
||||
```javascript
|
||||
// Solve ax + b = c for x
|
||||
function solveLinear(a, b, c) {
|
||||
const x = (c - b) / a;
|
||||
|
||||
return {
|
||||
equation: `${a}x + ${b} = ${c}`,
|
||||
steps: [
|
||||
`${a}x + ${b} = ${c}`,
|
||||
`${a}x = ${c - b}`,
|
||||
`x = ${(c - b)}/${a}`,
|
||||
`x = ${x}`
|
||||
],
|
||||
solution: x,
|
||||
verification: `${a}(${x}) + ${b} = ${a * x + b} ✓`
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Balance Scale Visualization
|
||||
```javascript
|
||||
function drawBalanceScale(left, right, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const centerX = canvas.width / 2;
|
||||
const centerY = canvas.height / 2;
|
||||
|
||||
// Calculate balance
|
||||
const diff = left - right;
|
||||
const angle = Math.atan(diff / 10);
|
||||
|
||||
// Draw fulcrum
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX - 20, centerY + 50);
|
||||
ctx.lineTo(centerX, centerY);
|
||||
ctx.lineTo(centerX + 20, centerY + 50);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Draw beam
|
||||
ctx.save();
|
||||
ctx.translate(centerX, centerY);
|
||||
ctx.rotate(angle);
|
||||
|
||||
ctx.fillStyle = '#8B4513';
|
||||
ctx.fillRect(-150, -10, 300, 20);
|
||||
|
||||
// Left side
|
||||
ctx.fillStyle = '#667eea';
|
||||
ctx.fillRect(-140, -60, 80, 80);
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.font = 'bold 24px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText(left, -100, -10);
|
||||
|
||||
// Right side
|
||||
ctx.fillStyle = '#FF6347';
|
||||
ctx.fillRect(60, -60, 80, 80);
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillText(right, 100, -10);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
// Status
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = '20px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
const status = Math.abs(diff) < 0.1 ? '⚖️ Balanced!' :
|
||||
diff > 0 ? '⬅️ Left heavier' : '➡️ Right heavier';
|
||||
ctx.fillText(status, centerX, centerY + 100);
|
||||
}
|
||||
```
|
||||
|
||||
## Quadratic Equations
|
||||
|
||||
### Quadratic Formula
|
||||
```javascript
|
||||
function solveQuadratic(a, b, c) {
|
||||
const discriminant = b * b - 4 * a * c;
|
||||
|
||||
const result = {
|
||||
equation: `${a}x² + ${b}x + ${c} = 0`,
|
||||
discriminant: discriminant,
|
||||
a, b, c
|
||||
};
|
||||
|
||||
if (discriminant > 0) {
|
||||
const x1 = (-b + Math.sqrt(discriminant)) / (2 * a);
|
||||
const x2 = (-b - Math.sqrt(discriminant)) / (2 * a);
|
||||
result.solutions = [x1, x2];
|
||||
result.type = 'Two real solutions';
|
||||
result.message = '🎉 Two x-intercepts!';
|
||||
} else if (discriminant === 0) {
|
||||
const x = -b / (2 * a);
|
||||
result.solutions = [x];
|
||||
result.type = 'One real solution';
|
||||
result.message = '🎯 One x-intercept (vertex on x-axis)!';
|
||||
} else {
|
||||
const realPart = -b / (2 * a);
|
||||
const imagPart = Math.sqrt(-discriminant) / (2 * a);
|
||||
result.solutions = [
|
||||
`${realPart} + ${imagPart}i`,
|
||||
`${realPart} - ${imagPart}i`
|
||||
];
|
||||
result.type = 'Two complex solutions';
|
||||
result.message = '👻 No x-intercepts (complex numbers)!';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Parabola Grapher
|
||||
```javascript
|
||||
function drawParabola(a, b, c, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
// Clear canvas
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
// Draw axes
|
||||
drawAxes(ctx, width, height);
|
||||
|
||||
// Calculate vertex
|
||||
const vertexX = -b / (2 * a);
|
||||
const vertexY = a * vertexX * vertexX + b * vertexX + c;
|
||||
|
||||
// Draw parabola
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.lineWidth = 3;
|
||||
|
||||
for (let x = -10; x <= 10; x += 0.1) {
|
||||
const y = a * x * x + b * x + c;
|
||||
const px = (x + 10) * (width / 20);
|
||||
const py = height - ((y + 10) * (height / 20));
|
||||
|
||||
if (x === -10) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Mark vertex
|
||||
const vx = (vertexX + 10) * (width / 20);
|
||||
const vy = height - ((vertexY + 10) * (height / 20));
|
||||
|
||||
ctx.fillStyle = '#FF6347';
|
||||
ctx.beginPath();
|
||||
ctx.arc(vx, vy, 8, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Vertex label
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = '14px Arial';
|
||||
ctx.fillText(`Vertex: (${vertexX.toFixed(2)}, ${vertexY.toFixed(2)})`,
|
||||
vx + 10, vy - 10);
|
||||
|
||||
// Draw axis of symmetry
|
||||
ctx.strokeStyle = '#FFD700';
|
||||
ctx.setLineDash([5, 5]);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vx, 0);
|
||||
ctx.lineTo(vx, height);
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
|
||||
return { vertexX, vertexY };
|
||||
}
|
||||
|
||||
function drawAxes(ctx, width, height) {
|
||||
ctx.strokeStyle = '#CCC';
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
// X-axis
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, height / 2);
|
||||
ctx.lineTo(width, height / 2);
|
||||
ctx.stroke();
|
||||
|
||||
// Y-axis
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(width / 2, 0);
|
||||
ctx.lineTo(width / 2, height);
|
||||
ctx.stroke();
|
||||
|
||||
// Grid
|
||||
ctx.strokeStyle = '#F0F0F0';
|
||||
for (let i = 0; i < width; i += width / 20) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(i, 0);
|
||||
ctx.lineTo(i, height);
|
||||
ctx.stroke();
|
||||
}
|
||||
for (let i = 0; i < height; i += height / 20) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, i);
|
||||
ctx.lineTo(width, i);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### Function Visualizer
|
||||
```javascript
|
||||
const commonFunctions = {
|
||||
linear: (x, m, b) => m * x + b,
|
||||
quadratic: (x, a, b, c) => a * x * x + b * x + c,
|
||||
cubic: (x, a) => a * x * x * x,
|
||||
exponential: (x, a) => Math.pow(a, x),
|
||||
logarithmic: (x, a) => Math.log(x) / Math.log(a),
|
||||
sine: (x, a) => a * Math.sin(x),
|
||||
cosine: (x, a) => a * Math.cos(x),
|
||||
absolute: (x) => Math.abs(x),
|
||||
squareRoot: (x) => Math.sqrt(x)
|
||||
};
|
||||
|
||||
function plotFunction(fn, xMin, xMax, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
drawAxes(ctx, width, height);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.lineWidth = 3;
|
||||
|
||||
const step = (xMax - xMin) / width;
|
||||
for (let x = xMin; x <= xMax; x += step) {
|
||||
try {
|
||||
const y = fn(x);
|
||||
if (!isNaN(y) && isFinite(y)) {
|
||||
const px = ((x - xMin) / (xMax - xMin)) * width;
|
||||
const py = height - ((y - (-10)) / 20) * height;
|
||||
|
||||
if (x === xMin) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
} catch (e) {
|
||||
// Skip invalid points
|
||||
}
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
```
|
||||
|
||||
### Domain & Range Finder
|
||||
```javascript
|
||||
function analyzeFunctionDomainRange(type, params) {
|
||||
const analysis = {
|
||||
type: type,
|
||||
params: params
|
||||
};
|
||||
|
||||
switch(type) {
|
||||
case 'linear':
|
||||
analysis.domain = '(-∞, ∞)';
|
||||
analysis.range = '(-∞, ∞)';
|
||||
break;
|
||||
case 'quadratic':
|
||||
const a = params.a;
|
||||
const vertex = -params.b / (2 * a);
|
||||
const vertexY = a * vertex * vertex + params.b * vertex + params.c;
|
||||
analysis.domain = '(-∞, ∞)';
|
||||
analysis.range = a > 0 ? `[${vertexY.toFixed(2)}, ∞)` :
|
||||
`(-∞, ${vertexY.toFixed(2)}]`;
|
||||
analysis.vertex = { x: vertex, y: vertexY };
|
||||
break;
|
||||
case 'squareRoot':
|
||||
analysis.domain = '[0, ∞)';
|
||||
analysis.range = '[0, ∞)';
|
||||
break;
|
||||
case 'logarithmic':
|
||||
analysis.domain = '(0, ∞)';
|
||||
analysis.range = '(-∞, ∞)';
|
||||
break;
|
||||
}
|
||||
|
||||
return analysis;
|
||||
}
|
||||
```
|
||||
|
||||
## Systems of Equations
|
||||
|
||||
### Graphical Solution
|
||||
```javascript
|
||||
function solveSystemGraphically(eq1, eq2, canvas) {
|
||||
// eq1 and eq2 are linear equations in form {m: slope, b: intercept}
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
drawAxes(ctx, width, height);
|
||||
|
||||
// Plot first equation
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
for (let x = -10; x <= 10; x += 0.1) {
|
||||
const y = eq1.m * x + eq1.b;
|
||||
const px = (x + 10) * (width / 20);
|
||||
const py = height - ((y + 10) * (height / 20));
|
||||
if (x === -10) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Plot second equation
|
||||
ctx.strokeStyle = '#FF6347';
|
||||
ctx.beginPath();
|
||||
for (let x = -10; x <= 10; x += 0.1) {
|
||||
const y = eq2.m * x + eq2.b;
|
||||
const px = (x + 10) * (width / 20);
|
||||
const py = height - ((y + 10) * (height / 20));
|
||||
if (x === -10) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Find intersection
|
||||
// eq1.m * x + eq1.b = eq2.m * x + eq2.b
|
||||
// (eq1.m - eq2.m) * x = eq2.b - eq1.b
|
||||
const x = (eq2.b - eq1.b) / (eq1.m - eq2.m);
|
||||
const y = eq1.m * x + eq1.b;
|
||||
|
||||
// Mark intersection
|
||||
const px = (x + 10) * (width / 20);
|
||||
const py = height - ((y + 10) * (height / 20));
|
||||
|
||||
ctx.fillStyle = '#FFD700';
|
||||
ctx.beginPath();
|
||||
ctx.arc(px, py, 10, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = 'bold 16px Arial';
|
||||
ctx.fillText(`(${x.toFixed(2)}, ${y.toFixed(2)})`, px + 15, py - 15);
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
```
|
||||
|
||||
## Polynomials
|
||||
|
||||
### Polynomial Evaluator
|
||||
```javascript
|
||||
function evaluatePolynomial(coefficients, x) {
|
||||
// coefficients = [a0, a1, a2, ...] for a0 + a1*x + a2*x^2 + ...
|
||||
let result = 0;
|
||||
let term = 1;
|
||||
|
||||
const terms = [];
|
||||
for (let i = 0; i < coefficients.length; i++) {
|
||||
const value = coefficients[i] * term;
|
||||
result += value;
|
||||
terms.push({
|
||||
coefficient: coefficients[i],
|
||||
power: i,
|
||||
value: value,
|
||||
display: i === 0 ? `${coefficients[i]}` :
|
||||
i === 1 ? `${coefficients[i]}x` :
|
||||
`${coefficients[i]}x^${i}`
|
||||
});
|
||||
term *= x;
|
||||
}
|
||||
|
||||
return {
|
||||
x: x,
|
||||
result: result,
|
||||
terms: terms,
|
||||
polynomial: formatPolynomial(coefficients)
|
||||
};
|
||||
}
|
||||
|
||||
function formatPolynomial(coefficients) {
|
||||
return coefficients.map((c, i) => {
|
||||
if (c === 0) return '';
|
||||
if (i === 0) return `${c}`;
|
||||
if (i === 1) return `${c}x`;
|
||||
return `${c}x^${i}`;
|
||||
}).filter(t => t).join(' + ');
|
||||
}
|
||||
```
|
||||
|
||||
## Interactive Elements
|
||||
|
||||
### Slider for Coefficients
|
||||
```javascript
|
||||
function createCoefficientSliders(equation) {
|
||||
const sliders = {};
|
||||
const params = ['a', 'b', 'c'];
|
||||
|
||||
params.forEach(param => {
|
||||
const slider = document.createElement('input');
|
||||
slider.type = 'range';
|
||||
slider.min = -10;
|
||||
slider.max = 10;
|
||||
slider.step = 0.1;
|
||||
slider.value = equation[param] || 0;
|
||||
|
||||
slider.addEventListener('input', () => {
|
||||
equation[param] = parseFloat(slider.value);
|
||||
updateGraph();
|
||||
updateEquationDisplay();
|
||||
});
|
||||
|
||||
sliders[param] = slider;
|
||||
});
|
||||
|
||||
return sliders;
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Algebra patterns provide:
|
||||
- Visual equation solving with balance scales
|
||||
- Interactive function graphing
|
||||
- Step-by-step solutions
|
||||
- Real-time parameter manipulation
|
||||
- Geometric representations of algebraic concepts
|
||||
|
||||
These tools make abstract algebra concrete and interactive!
|
||||
370
skills/math-teacher/references/basic_math.md
Normal file
370
skills/math-teacher/references/basic_math.md
Normal file
@@ -0,0 +1,370 @@
|
||||
# Basic Math Reference
|
||||
|
||||
Elementary mathematics concepts for interactive playgrounds.
|
||||
|
||||
## Arithmetic Operations
|
||||
|
||||
### Addition
|
||||
```javascript
|
||||
// Visual addition with counting blocks
|
||||
function visualAdd(a, b) {
|
||||
return {
|
||||
result: a + b,
|
||||
visualization: `${'🟦'.repeat(a)} + ${'🟨'.repeat(b)} = ${'🟩'.repeat(a + b)}`
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Multiplication
|
||||
```javascript
|
||||
// Array/grid visualization
|
||||
function visualMultiply(rows, cols) {
|
||||
let grid = '';
|
||||
for (let i = 0; i < rows; i++) {
|
||||
grid += '⬜'.repeat(cols) + '\n';
|
||||
}
|
||||
return {
|
||||
result: rows * cols,
|
||||
grid: grid,
|
||||
explanation: `${rows} rows × ${cols} columns = ${rows * cols} squares`
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Fractions
|
||||
|
||||
### Visual Representation
|
||||
```javascript
|
||||
function drawFraction(numerator, denominator, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
const sliceWidth = width / denominator;
|
||||
|
||||
for (let i = 0; i < denominator; i++) {
|
||||
ctx.fillStyle = i < numerator ? '#FF6347' : '#DDD';
|
||||
ctx.fillRect(i * sliceWidth, 0, sliceWidth - 2, height);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Equivalent Fractions
|
||||
```javascript
|
||||
function findEquivalent(numerator, denominator, multiplier) {
|
||||
return {
|
||||
original: `${numerator}/${denominator}`,
|
||||
equivalent: `${numerator * multiplier}/${denominator * multiplier}`,
|
||||
explanation: `Multiply both by ${multiplier}`
|
||||
};
|
||||
}
|
||||
|
||||
function simplify(numerator, denominator) {
|
||||
const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
|
||||
const divisor = gcd(numerator, denominator);
|
||||
return {
|
||||
original: `${numerator}/${denominator}`,
|
||||
simplified: `${numerator / divisor}/${denominator / divisor}`,
|
||||
divisor: divisor
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Geometry
|
||||
|
||||
### Area Calculations
|
||||
```javascript
|
||||
// Rectangle
|
||||
function rectangleArea(length, width) {
|
||||
return {
|
||||
area: length * width,
|
||||
perimeter: 2 * (length + width),
|
||||
visualization: drawRectangle(length, width)
|
||||
};
|
||||
}
|
||||
|
||||
// Circle
|
||||
function circleArea(radius) {
|
||||
return {
|
||||
area: Math.PI * radius * radius,
|
||||
circumference: 2 * Math.PI * radius,
|
||||
diameter: 2 * radius
|
||||
};
|
||||
}
|
||||
|
||||
// Triangle
|
||||
function triangleArea(base, height) {
|
||||
return {
|
||||
area: 0.5 * base * height,
|
||||
explanation: 'Half of base × height'
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Angle Visualizer
|
||||
```javascript
|
||||
function drawAngle(degrees, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const centerX = canvas.width / 2;
|
||||
const centerY = canvas.height / 2;
|
||||
const radius = 100;
|
||||
|
||||
// Draw angle arc
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, radius, 0, degrees * Math.PI / 180);
|
||||
ctx.lineTo(centerX, centerY);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(102, 126, 234, 0.3)';
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.stroke();
|
||||
|
||||
// Label
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = '20px Arial';
|
||||
ctx.fillText(`${degrees}°`, centerX + 50, centerY - 50);
|
||||
}
|
||||
```
|
||||
|
||||
## Number Patterns
|
||||
|
||||
### Sequences
|
||||
```javascript
|
||||
// Arithmetic sequence
|
||||
function arithmeticSequence(start, difference, terms) {
|
||||
const sequence = [];
|
||||
for (let i = 0; i < terms; i++) {
|
||||
sequence.push(start + (i * difference));
|
||||
}
|
||||
return {
|
||||
sequence: sequence,
|
||||
rule: `Start at ${start}, add ${difference} each time`,
|
||||
next: start + (terms * difference)
|
||||
};
|
||||
}
|
||||
|
||||
// Geometric sequence
|
||||
function geometricSequence(start, ratio, terms) {
|
||||
const sequence = [];
|
||||
for (let i = 0; i < terms; i++) {
|
||||
sequence.push(start * Math.pow(ratio, i));
|
||||
}
|
||||
return {
|
||||
sequence: sequence,
|
||||
rule: `Start at ${start}, multiply by ${ratio} each time`,
|
||||
next: start * Math.pow(ratio, terms)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Time & Money
|
||||
|
||||
### Clock
|
||||
```javascript
|
||||
function drawClock(hours, minutes) {
|
||||
// Draw clock face with hands
|
||||
const hourAngle = (hours % 12) * 30 + minutes * 0.5;
|
||||
const minuteAngle = minutes * 6;
|
||||
|
||||
return {
|
||||
hourAngle: hourAngle,
|
||||
minuteAngle: minuteAngle,
|
||||
timeString: `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Money
|
||||
```javascript
|
||||
function calculateChange(paid, cost) {
|
||||
const change = paid - cost;
|
||||
const bills = {
|
||||
'20': Math.floor(change / 20),
|
||||
'10': Math.floor((change % 20) / 10),
|
||||
'5': Math.floor((change % 10) / 5),
|
||||
'1': Math.floor(change % 5)
|
||||
};
|
||||
|
||||
return {
|
||||
change: change.toFixed(2),
|
||||
breakdown: bills,
|
||||
visualization: visualizeChange(bills)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Interactive Elements
|
||||
|
||||
### Number Line
|
||||
```javascript
|
||||
function drawNumberLine(min, max, highlight, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
const y = height / 2;
|
||||
|
||||
// Draw line
|
||||
ctx.strokeStyle = '#333';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(20, y);
|
||||
ctx.lineTo(width - 20, y);
|
||||
ctx.stroke();
|
||||
|
||||
// Draw numbers
|
||||
const range = max - min;
|
||||
const step = (width - 40) / range;
|
||||
|
||||
for (let i = min; i <= max; i++) {
|
||||
const x = 20 + (i - min) * step;
|
||||
|
||||
// Tick mark
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y - 10);
|
||||
ctx.lineTo(x, y + 10);
|
||||
ctx.stroke();
|
||||
|
||||
// Number label
|
||||
ctx.fillStyle = i === highlight ? '#FF6347' : '#333';
|
||||
ctx.font = i === highlight ? 'bold 20px Arial' : '16px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText(i, x, y + 30);
|
||||
|
||||
// Highlight
|
||||
if (i === highlight) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, 8, 0, Math.PI * 2);
|
||||
ctx.fillStyle = '#FF6347';
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Counting Blocks
|
||||
```javascript
|
||||
function drawCountingBlocks(number, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const blockSize = 40;
|
||||
const gap = 10;
|
||||
const blocksPerRow = 10;
|
||||
|
||||
for (let i = 0; i < number; i++) {
|
||||
const row = Math.floor(i / blocksPerRow);
|
||||
const col = i % blocksPerRow;
|
||||
const x = col * (blockSize + gap);
|
||||
const y = row * (blockSize + gap);
|
||||
|
||||
// Draw block
|
||||
ctx.fillStyle = '#667eea';
|
||||
ctx.fillRect(x, y, blockSize, blockSize);
|
||||
ctx.strokeStyle = '#333';
|
||||
ctx.strokeRect(x, y, blockSize, blockSize);
|
||||
|
||||
// Number in block
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.font = 'bold 20px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(i + 1, x + blockSize / 2, y + blockSize / 2);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Word Problems
|
||||
|
||||
### Template System
|
||||
```javascript
|
||||
const wordProblemTemplates = {
|
||||
addition: [
|
||||
"🍎 Sarah has {a} apples. Her friend gives her {b} more. How many apples does Sarah have now?",
|
||||
"🚗 There are {a} cars in the parking lot. {b} more cars arrive. How many cars are there in total?",
|
||||
"⭐ Tom collected {a} stars. He then collected {b} more stars. How many stars does Tom have?"
|
||||
],
|
||||
|
||||
subtraction: [
|
||||
"🍪 Mom baked {a} cookies. The family ate {b} cookies. How many cookies are left?",
|
||||
"🎈 There were {a} balloons at the party. {b} balloons popped. How many balloons remain?",
|
||||
"💰 You have ${a}. You spend ${b}. How much money do you have left?"
|
||||
],
|
||||
|
||||
multiplication: [
|
||||
"📦 Each box contains {a} toys. You have {b} boxes. How many toys do you have in total?",
|
||||
"🌳 Each tree has {a} apples. There are {b} trees. How many apples are there?",
|
||||
"👥 Each team has {a} players. There are {b} teams. How many players in total?"
|
||||
],
|
||||
|
||||
division: [
|
||||
"🍕 You have {a} pizza slices to share equally among {b} friends. How many slices does each friend get?",
|
||||
"🎁 {a} presents need to be distributed equally to {b} children. How many presents per child?",
|
||||
"📚 {a} books are arranged into {b} equal stacks. How many books in each stack?"
|
||||
]
|
||||
};
|
||||
|
||||
function generateWordProblem(type, difficulty) {
|
||||
const template = wordProblemTemplates[type][
|
||||
Math.floor(Math.random() * wordProblemTemplates[type].length)
|
||||
];
|
||||
|
||||
const range = difficulty === 'easy' ? 10 : difficulty === 'medium' ? 50 : 100;
|
||||
const a = Math.floor(Math.random() * range) + 1;
|
||||
const b = Math.floor(Math.random() * range) + 1;
|
||||
|
||||
const problem = template.replace('{a}', a).replace('{b}', b);
|
||||
const answer = calculateAnswer(type, a, b);
|
||||
|
||||
return { problem, answer, a, b };
|
||||
}
|
||||
|
||||
function calculateAnswer(type, a, b) {
|
||||
switch(type) {
|
||||
case 'addition': return a + b;
|
||||
case 'subtraction': return a - b;
|
||||
case 'multiplication': return a * b;
|
||||
case 'division': return Math.floor(a / b);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Game Mechanics
|
||||
|
||||
### Points System
|
||||
```javascript
|
||||
const scoring = {
|
||||
correct: {
|
||||
first_try: 10,
|
||||
second_try: 7,
|
||||
third_try: 5,
|
||||
with_hint: 3
|
||||
},
|
||||
streak: {
|
||||
multiplier: (streak) => Math.min(1 + (streak * 0.1), 3.0)
|
||||
},
|
||||
speed: {
|
||||
bonus: (seconds) => seconds < 5 ? 5 : seconds < 10 ? 3 : 0
|
||||
}
|
||||
};
|
||||
|
||||
function calculatePoints(attempt, streak, seconds, usedHint) {
|
||||
let basePoints = scoring.correct.first_try;
|
||||
if (usedHint) basePoints = scoring.correct.with_hint;
|
||||
else if (attempt === 2) basePoints = scoring.correct.second_try;
|
||||
else if (attempt >= 3) basePoints = scoring.correct.third_try;
|
||||
|
||||
const streakMultiplier = scoring.streak.multiplier(streak);
|
||||
const speedBonus = scoring.speed.bonus(seconds);
|
||||
|
||||
return Math.floor(basePoints * streakMultiplier) + speedBonus;
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
These basic math patterns provide:
|
||||
- Visual representations for concrete understanding
|
||||
- Interactive manipulatives for hands-on learning
|
||||
- Gamification elements for engagement
|
||||
- Progressive difficulty scaffolding
|
||||
- Clear explanations with every calculation
|
||||
|
||||
Use these as building blocks for creating engaging math playgrounds!
|
||||
565
skills/math-teacher/references/calculus.md
Normal file
565
skills/math-teacher/references/calculus.md
Normal file
@@ -0,0 +1,565 @@
|
||||
# Calculus Reference
|
||||
|
||||
Advanced calculus concepts with interactive visualizations.
|
||||
|
||||
## Derivatives
|
||||
|
||||
### Derivative Calculator
|
||||
```javascript
|
||||
// Numerical derivative using limit definition
|
||||
function numericalDerivative(f, x, h = 0.0001) {
|
||||
return (f(x + h) - f(x - h)) / (2 * h);
|
||||
}
|
||||
|
||||
// Common derivatives
|
||||
const derivatives = {
|
||||
power: (n) => (x) => n * Math.pow(x, n - 1),
|
||||
exponential: (a) => (x) => a * Math.pow(Math.E, a * x),
|
||||
logarithmic: (x) => 1 / x,
|
||||
sine: (x) => Math.cos(x),
|
||||
cosine: (x) => -Math.sin(x),
|
||||
tangent: (x) => 1 / Math.pow(Math.cos(x), 2)
|
||||
};
|
||||
```
|
||||
|
||||
### Tangent Line Visualizer
|
||||
```javascript
|
||||
function drawTangentLine(f, x0, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
// Calculate derivative at x0
|
||||
const slope = numericalDerivative(f, x0);
|
||||
const y0 = f(x0);
|
||||
|
||||
// Draw function
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
|
||||
for (let x = -5; x <= 5; x += 0.1) {
|
||||
const y = f(x);
|
||||
const px = (x + 5) * (width / 10);
|
||||
const py = height - ((y + 5) * (height / 10));
|
||||
|
||||
if (x === -5) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Draw tangent line
|
||||
ctx.strokeStyle = '#FF6347';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
|
||||
for (let x = -5; x <= 5; x += 0.1) {
|
||||
const y = y0 + slope * (x - x0); // Point-slope form
|
||||
const px = (x + 5) * (width / 10);
|
||||
const py = height - ((y + 5) * (height / 10));
|
||||
|
||||
if (x === -5) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Mark point of tangency
|
||||
const px0 = (x0 + 5) * (width / 10);
|
||||
const py0 = height - ((y0 + 5) * (height / 10));
|
||||
|
||||
ctx.fillStyle = '#FFD700';
|
||||
ctx.beginPath();
|
||||
ctx.arc(px0, py0, 8, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Display slope
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = 'bold 16px Arial';
|
||||
ctx.fillText(`Slope = ${slope.toFixed(3)}`, 20, 30);
|
||||
ctx.fillText(`f'(${x0.toFixed(2)}) = ${slope.toFixed(3)}`, 20, 50);
|
||||
|
||||
return { slope, y0, x0 };
|
||||
}
|
||||
```
|
||||
|
||||
### Secant Line Animation
|
||||
```javascript
|
||||
function animateSecantToTangent(f, x0, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
let h = 2; // Start with h = 2
|
||||
|
||||
function animate() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Draw function
|
||||
drawFunction(f, canvas);
|
||||
|
||||
// Calculate secant slope
|
||||
const y0 = f(x0);
|
||||
const y1 = f(x0 + h);
|
||||
const slope = (y1 - y0) / h;
|
||||
|
||||
// Draw secant line
|
||||
drawSecantLine(x0, y0, x0 + h, y1, canvas);
|
||||
|
||||
// Display values
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = 'bold 16px Arial';
|
||||
ctx.fillText(`h = ${h.toFixed(4)}`, 20, 30);
|
||||
ctx.fillText(`Secant slope = ${slope.toFixed(4)}`, 20, 50);
|
||||
|
||||
if (h > 0.0001) {
|
||||
h *= 0.95; // Gradually decrease h
|
||||
setTimeout(animate, 50);
|
||||
} else {
|
||||
ctx.fillText('→ Tangent line!', 20, 70);
|
||||
const derivative = numericalDerivative(f, x0);
|
||||
ctx.fillText(`Derivative = ${derivative.toFixed(4)}`, 20, 90);
|
||||
}
|
||||
}
|
||||
|
||||
animate();
|
||||
}
|
||||
```
|
||||
|
||||
### Rate of Change Explorer
|
||||
```javascript
|
||||
function exploreRateOfChange(scenario, canvas) {
|
||||
// Scenarios: distance-time → velocity, velocity-time → acceleration, etc.
|
||||
const scenarios = {
|
||||
position: {
|
||||
f: (t) => 5 * t * t, // Position function
|
||||
derivative: 'Velocity',
|
||||
units: 'm/s',
|
||||
explanation: 'How fast position changes = velocity'
|
||||
},
|
||||
velocity: {
|
||||
f: (t) => 10 * t, // Velocity function
|
||||
derivative: 'Acceleration',
|
||||
units: 'm/s²',
|
||||
explanation: 'How fast velocity changes = acceleration'
|
||||
},
|
||||
population: {
|
||||
f: (t) => 1000 * Math.exp(0.1 * t), // Exponential growth
|
||||
derivative: 'Growth Rate',
|
||||
units: 'people/year',
|
||||
explanation: 'How fast population changes = growth rate'
|
||||
}
|
||||
};
|
||||
|
||||
const s = scenarios[scenario];
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Draw both function and derivative
|
||||
drawTwoGraphs(s.f, (x) => numericalDerivative(s.f, x), canvas);
|
||||
|
||||
return s;
|
||||
}
|
||||
```
|
||||
|
||||
## Integrals
|
||||
|
||||
### Riemann Sum Visualizer
|
||||
```javascript
|
||||
function drawRiemannSum(f, a, b, n, method, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
const dx = (b - a) / n;
|
||||
|
||||
let sum = 0;
|
||||
|
||||
// Draw function
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
|
||||
for (let x = a; x <= b; x += 0.01) {
|
||||
const y = f(x);
|
||||
const px = ((x - a) / (b - a)) * width;
|
||||
const py = height - (y / 10) * height;
|
||||
|
||||
if (x === a) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Draw rectangles
|
||||
for (let i = 0; i < n; i++) {
|
||||
const x = a + i * dx;
|
||||
let sampleX;
|
||||
|
||||
switch(method) {
|
||||
case 'left':
|
||||
sampleX = x;
|
||||
break;
|
||||
case 'right':
|
||||
sampleX = x + dx;
|
||||
break;
|
||||
case 'midpoint':
|
||||
sampleX = x + dx / 2;
|
||||
break;
|
||||
case 'trapezoid':
|
||||
// Will handle separately
|
||||
break;
|
||||
}
|
||||
|
||||
if (method !== 'trapezoid') {
|
||||
const height_rect = f(sampleX);
|
||||
sum += height_rect * dx;
|
||||
|
||||
// Draw rectangle
|
||||
const px = ((x - a) / (b - a)) * width;
|
||||
const pw = (dx / (b - a)) * width;
|
||||
const ph = (height_rect / 10) * height;
|
||||
|
||||
ctx.fillStyle = 'rgba(102, 126, 234, 0.3)';
|
||||
ctx.fillRect(px, height - ph, pw, ph);
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.strokeRect(px, height - ph, pw, ph);
|
||||
}
|
||||
}
|
||||
|
||||
// Display sum
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = 'bold 18px Arial';
|
||||
ctx.fillText(`Rectangles: ${n}`, 20, 30);
|
||||
ctx.fillText(`Approximate Area: ${sum.toFixed(4)}`, 20, 55);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
function animateRiemannIncrease(f, a, b, canvas) {
|
||||
let n = 1;
|
||||
|
||||
function animate() {
|
||||
const sum = drawRiemannSum(f, a, b, n, 'midpoint', canvas);
|
||||
|
||||
if (n < 100) {
|
||||
n++;
|
||||
setTimeout(animate, 100);
|
||||
} else {
|
||||
// Show exact integral
|
||||
const exact = exactIntegral(f, a, b);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#FF6347';
|
||||
ctx.font = 'bold 18px Arial';
|
||||
ctx.fillText(`Exact Area: ${exact.toFixed(4)}`, 20, 80);
|
||||
}
|
||||
}
|
||||
|
||||
animate();
|
||||
}
|
||||
```
|
||||
|
||||
### Area Under Curve
|
||||
```javascript
|
||||
function calculateDefiniteIntegral(f, a, b, method = 'simpson') {
|
||||
const n = 1000; // Number of subdivisions
|
||||
const h = (b - a) / n;
|
||||
let sum = 0;
|
||||
|
||||
switch(method) {
|
||||
case 'simpson':
|
||||
// Simpson's Rule (most accurate)
|
||||
sum = f(a) + f(b);
|
||||
for (let i = 1; i < n; i++) {
|
||||
const x = a + i * h;
|
||||
sum += (i % 2 === 0 ? 2 : 4) * f(x);
|
||||
}
|
||||
sum *= h / 3;
|
||||
break;
|
||||
|
||||
case 'trapezoidal':
|
||||
sum = (f(a) + f(b)) / 2;
|
||||
for (let i = 1; i < n; i++) {
|
||||
sum += f(a + i * h);
|
||||
}
|
||||
sum *= h;
|
||||
break;
|
||||
|
||||
case 'midpoint':
|
||||
for (let i = 0; i < n; i++) {
|
||||
sum += f(a + (i + 0.5) * h);
|
||||
}
|
||||
sum *= h;
|
||||
break;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
```
|
||||
|
||||
### Accumulation Function
|
||||
```javascript
|
||||
function drawAccumulationFunction(f, a, canvas) {
|
||||
// Draw A(x) = ∫[a to x] f(t) dt
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
// Original function f
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
|
||||
for (let x = -5; x <= 5; x += 0.1) {
|
||||
const y = f(x);
|
||||
const px = (x + 5) * (width / 10);
|
||||
const py = height / 2 - y * 20;
|
||||
|
||||
if (x === -5) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Accumulation function A(x)
|
||||
ctx.strokeStyle = '#FF6347';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
|
||||
for (let x = -5; x <= 5; x += 0.1) {
|
||||
const y = calculateDefiniteIntegral(f, a, x);
|
||||
const px = (x + 5) * (width / 10);
|
||||
const py = height / 2 - y * 20;
|
||||
|
||||
if (x === -5) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Labels
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = '16px Arial';
|
||||
ctx.fillText('f(x) - Original function', 20, 30);
|
||||
ctx.fillStyle = '#FF6347';
|
||||
ctx.fillText('A(x) = ∫f(t)dt - Accumulation', 20, 55);
|
||||
}
|
||||
```
|
||||
|
||||
## Limits
|
||||
|
||||
### Limit Visualizer
|
||||
```javascript
|
||||
function visualizeLimit(f, a, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
// Draw function
|
||||
ctx.strokeStyle = '#667eea';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
|
||||
let leftLimit = null;
|
||||
let rightLimit = null;
|
||||
|
||||
for (let x = -5; x <= 5; x += 0.01) {
|
||||
if (Math.abs(x - a) > 0.001) { // Skip the point
|
||||
const y = f(x);
|
||||
if (isFinite(y)) {
|
||||
const px = (x + 5) * (width / 10);
|
||||
const py = height / 2 - y * 20;
|
||||
|
||||
if (x === -5) ctx.moveTo(px, py);
|
||||
else ctx.lineTo(px, py);
|
||||
|
||||
// Track limits
|
||||
if (x < a && x > a - 0.1) leftLimit = y;
|
||||
if (x > a && x < a + 0.1) rightLimit = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Mark point of interest
|
||||
const px = (a + 5) * (width / 10);
|
||||
const py_left = height / 2 - leftLimit * 20;
|
||||
const py_right = height / 2 - rightLimit * 20;
|
||||
|
||||
// Open circles for limit points
|
||||
ctx.strokeStyle = '#FF6347';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
ctx.arc(px, py_left, 6, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
|
||||
// Display limit values
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = 'bold 16px Arial';
|
||||
ctx.fillText(`lim (x→${a}⁻) = ${leftLimit?.toFixed(3)}`, 20, 30);
|
||||
ctx.fillText(`lim (x→${a}⁺) = ${rightLimit?.toFixed(3)}`, 20, 55);
|
||||
|
||||
const limitExists = Math.abs(leftLimit - rightLimit) < 0.01;
|
||||
if (limitExists) {
|
||||
ctx.fillStyle = '#4CAF50';
|
||||
ctx.fillText(`Limit exists: ${leftLimit.toFixed(3)}`, 20, 80);
|
||||
} else {
|
||||
ctx.fillStyle = '#FF6347';
|
||||
ctx.fillText('Limit does not exist', 20, 80);
|
||||
}
|
||||
|
||||
return { leftLimit, rightLimit, limitExists };
|
||||
}
|
||||
```
|
||||
|
||||
### Interactive Epsilon-Delta
|
||||
```javascript
|
||||
function visualizeEpsilonDelta(f, a, L, epsilon, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Draw function
|
||||
drawFunction(f, canvas);
|
||||
|
||||
// Draw epsilon band
|
||||
const py_upper = height / 2 - (L + epsilon) * 20;
|
||||
const py_lower = height / 2 - (L - epsilon) * 20;
|
||||
|
||||
ctx.fillStyle = 'rgba(255, 99, 71, 0.2)';
|
||||
ctx.fillRect(0, py_upper, width, py_lower - py_upper);
|
||||
|
||||
// Find delta
|
||||
let delta = 0.1;
|
||||
for (let d = 0.01; d < 5; d += 0.01) {
|
||||
const y_left = f(a - d);
|
||||
const y_right = f(a + d);
|
||||
|
||||
if (Math.abs(y_left - L) < epsilon && Math.abs(y_right - L) < epsilon) {
|
||||
delta = d;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw delta interval
|
||||
const px_left = (a - delta + 5) * (width / 10);
|
||||
const px_right = (a + delta + 5) * (width / 10);
|
||||
|
||||
ctx.fillStyle = 'rgba(102, 126, 234, 0.2)';
|
||||
ctx.fillRect(px_left, 0, px_right - px_left, height);
|
||||
|
||||
// Labels
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = 'bold 16px Arial';
|
||||
ctx.fillText(`ε = ${epsilon.toFixed(3)}`, 20, 30);
|
||||
ctx.fillText(`δ = ${delta.toFixed(3)}`, 20, 55);
|
||||
ctx.fillText(`For |x - ${a}| < δ, |f(x) - ${L}| < ε`, 20, 80);
|
||||
|
||||
return delta;
|
||||
}
|
||||
```
|
||||
|
||||
## Optimization
|
||||
|
||||
### Critical Points Finder
|
||||
```javascript
|
||||
function findCriticalPoints(f, a, b) {
|
||||
const points = [];
|
||||
const step = (b - a) / 1000;
|
||||
|
||||
for (let x = a; x <= b; x += step) {
|
||||
const derivative = numericalDerivative(f, x);
|
||||
|
||||
// Check if derivative is close to zero
|
||||
if (Math.abs(derivative) < 0.01) {
|
||||
const secondDerivative = numericalDerivative(
|
||||
(t) => numericalDerivative(f, t),
|
||||
x
|
||||
);
|
||||
|
||||
points.push({
|
||||
x: x,
|
||||
y: f(x),
|
||||
type: secondDerivative < 0 ? 'local maximum' :
|
||||
secondDerivative > 0 ? 'local minimum' :
|
||||
'inflection point',
|
||||
derivative: derivative,
|
||||
secondDerivative: secondDerivative
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
function visualizeOptimization(f, a, b, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Draw function
|
||||
drawFunction(f, canvas);
|
||||
|
||||
// Find and mark critical points
|
||||
const criticalPoints = findCriticalPoints(f, a, b);
|
||||
|
||||
criticalPoints.forEach(point => {
|
||||
const px = (point.x + 5) * (width / 10);
|
||||
const py = height / 2 - point.y * 20;
|
||||
|
||||
// Different colors for different types
|
||||
ctx.fillStyle = point.type.includes('maximum') ? '#4CAF50' :
|
||||
point.type.includes('minimum') ? '#FF6347' :
|
||||
'#FFD700';
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(px, py, 8, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Label
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = '12px Arial';
|
||||
ctx.fillText(`${point.type}`, px + 10, py - 10);
|
||||
ctx.fillText(`(${point.x.toFixed(2)}, ${point.y.toFixed(2)})`,
|
||||
px + 10, py + 5);
|
||||
});
|
||||
|
||||
return criticalPoints;
|
||||
}
|
||||
```
|
||||
|
||||
## Related Rates
|
||||
|
||||
### Related Rates Visualizer
|
||||
```javascript
|
||||
function visualizeRelatedRates(scenario, rate, time, canvas) {
|
||||
const scenarios = {
|
||||
ladder: {
|
||||
// Ladder sliding down wall
|
||||
length: 10, // Ladder length
|
||||
rate: rate, // dx/dt (horizontal speed)
|
||||
draw: function(t) {
|
||||
const x = rate * t;
|
||||
const y = Math.sqrt(this.length * this.length - x * x);
|
||||
const dy_dt = -(x * rate) / y; // dy/dt calculated
|
||||
|
||||
drawLadder(x, y, canvas);
|
||||
return { x, y, dx_dt: rate, dy_dt };
|
||||
}
|
||||
},
|
||||
balloon: {
|
||||
// Expanding balloon
|
||||
rate: rate, // dr/dt (radius growth rate)
|
||||
draw: function(t) {
|
||||
const r = 1 + rate * t;
|
||||
const V = (4/3) * Math.PI * r * r * r;
|
||||
const dV_dt = 4 * Math.PI * r * r * rate; // dV/dt
|
||||
|
||||
drawBalloon(r, canvas);
|
||||
return { r, V, dr_dt: rate, dV_dt };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return scenarios[scenario].draw(time);
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Calculus patterns provide:
|
||||
- Visual derivative calculations with tangent lines
|
||||
- Riemann sum animations showing integral approximation
|
||||
- Limit visualization with epsilon-delta
|
||||
- Critical point finding for optimization
|
||||
- Related rates scenarios with animations
|
||||
|
||||
These tools make abstract calculus concepts concrete and interactive!
|
||||
635
skills/math-teacher/references/gamification.md
Normal file
635
skills/math-teacher/references/gamification.md
Normal file
@@ -0,0 +1,635 @@
|
||||
# Gamification Reference
|
||||
|
||||
Game mechanics, reward systems, and engagement techniques for math learning.
|
||||
|
||||
## Points System
|
||||
|
||||
### Base Points
|
||||
```javascript
|
||||
const POINTS = {
|
||||
// Correctness
|
||||
CORRECT_FIRST_TRY: 10,
|
||||
CORRECT_SECOND_TRY: 7,
|
||||
CORRECT_THIRD_TRY: 5,
|
||||
CORRECT_WITH_HINT: 3,
|
||||
|
||||
// Exploration
|
||||
TRY_DIFFERENT_VALUE: 2,
|
||||
DISCOVER_PATTERN: 15,
|
||||
FIND_SPECIAL_CASE: 20,
|
||||
|
||||
// Speed
|
||||
SPEED_BONUS_5SEC: 5,
|
||||
SPEED_BONUS_10SEC: 3,
|
||||
SPEED_BONUS_30SEC: 1,
|
||||
|
||||
// Completion
|
||||
COMPLETE_LEVEL: 50,
|
||||
COMPLETE_CHALLENGE: 100,
|
||||
PERFECT_SCORE: 200,
|
||||
|
||||
// Daily
|
||||
DAILY_LOGIN: 10,
|
||||
DAILY_CHALLENGE: 25
|
||||
};
|
||||
```
|
||||
|
||||
### Multipliers
|
||||
```javascript
|
||||
function calculateMultiplier(state) {
|
||||
let multiplier = 1.0;
|
||||
|
||||
// Streak multiplier
|
||||
if (state.streak >= 3) multiplier += 0.5;
|
||||
if (state.streak >= 5) multiplier += 0.5;
|
||||
if (state.streak >= 10) multiplier += 1.0;
|
||||
|
||||
// Difficulty multiplier
|
||||
multiplier *= {
|
||||
'easy': 1.0,
|
||||
'medium': 1.5,
|
||||
'hard': 2.0,
|
||||
'expert': 3.0
|
||||
}[state.difficulty] || 1.0;
|
||||
|
||||
// Level multiplier
|
||||
multiplier *= (1 + (state.level * 0.1));
|
||||
|
||||
return Math.min(multiplier, 5.0); // Cap at 5x
|
||||
}
|
||||
```
|
||||
|
||||
### Total Score Calculation
|
||||
```javascript
|
||||
function awardPoints(basePoints, state) {
|
||||
const multiplier = calculateMultiplier(state);
|
||||
const speedBonus = calculateSpeedBonus(state.timeSpent);
|
||||
const total = Math.floor(basePoints * multiplier) + speedBonus;
|
||||
|
||||
return {
|
||||
base: basePoints,
|
||||
multiplier: multiplier,
|
||||
speedBonus: speedBonus,
|
||||
total: total,
|
||||
breakdown: `${basePoints} × ${multiplier.toFixed(1)} + ${speedBonus}`
|
||||
};
|
||||
}
|
||||
|
||||
function calculateSpeedBonus(seconds) {
|
||||
if (seconds < 5) return POINTS.SPEED_BONUS_5SEC;
|
||||
if (seconds < 10) return POINTS.SPEED_BONUS_10SEC;
|
||||
if (seconds < 30) return POINTS.SPEED_BONUS_30SEC;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Achievements
|
||||
|
||||
### Achievement Definitions
|
||||
```javascript
|
||||
const ACHIEVEMENTS = {
|
||||
first_steps: {
|
||||
id: 'first_steps',
|
||||
name: '🌟 First Steps',
|
||||
description: 'Complete your first problem',
|
||||
condition: (stats) => stats.problemsCompleted >= 1,
|
||||
points: 10,
|
||||
rarity: 'common'
|
||||
},
|
||||
|
||||
hot_streak: {
|
||||
id: 'hot_streak',
|
||||
name: '🔥 Hot Streak',
|
||||
description: 'Get 5 correct answers in a row',
|
||||
condition: (stats) => stats.currentStreak >= 5,
|
||||
points: 25,
|
||||
rarity: 'uncommon'
|
||||
},
|
||||
|
||||
speed_demon: {
|
||||
id: 'speed_demon',
|
||||
name: '⚡ Speed Demon',
|
||||
description: 'Solve 10 problems in under 5 seconds each',
|
||||
condition: (stats) => stats.fastSolves >= 10,
|
||||
points: 50,
|
||||
rarity: 'rare'
|
||||
},
|
||||
|
||||
perfectionist: {
|
||||
id: 'perfectionist',
|
||||
name: '💯 Perfectionist',
|
||||
description: 'Get 100% on a challenge',
|
||||
condition: (stats) => stats.perfectScores >= 1,
|
||||
points: 100,
|
||||
rarity: 'epic'
|
||||
},
|
||||
|
||||
math_master: {
|
||||
id: 'math_master',
|
||||
name: '🏆 Math Master',
|
||||
description: 'Reach 1000 total points',
|
||||
condition: (stats) => stats.totalPoints >= 1000,
|
||||
points: 200,
|
||||
rarity: 'legendary'
|
||||
},
|
||||
|
||||
explorer: {
|
||||
id: 'explorer',
|
||||
name: '🔍 Explorer',
|
||||
description: 'Try 50 different values',
|
||||
condition: (stats) => stats.valuesExplored >= 50,
|
||||
points: 30,
|
||||
rarity: 'uncommon'
|
||||
},
|
||||
|
||||
pattern_finder: {
|
||||
id: 'pattern_finder',
|
||||
name: '🧩 Pattern Finder',
|
||||
description: 'Discover 5 mathematical patterns',
|
||||
condition: (stats) => stats.patternsFound >= 5,
|
||||
points: 75,
|
||||
rarity: 'rare'
|
||||
},
|
||||
|
||||
night_owl: {
|
||||
id: 'night_owl',
|
||||
name: '🦉 Night Owl',
|
||||
description: 'Practice after midnight',
|
||||
condition: (stats) => stats.lateNightSessions >= 1,
|
||||
points: 15,
|
||||
rarity: 'common'
|
||||
},
|
||||
|
||||
early_bird: {
|
||||
id: 'early_bird',
|
||||
name: '🐦 Early Bird',
|
||||
description: 'Practice before 6 AM',
|
||||
condition: (stats) => stats.earlyMorningSessions >= 1,
|
||||
points: 15,
|
||||
rarity: 'common'
|
||||
},
|
||||
|
||||
week_warrior: {
|
||||
id: 'week_warrior',
|
||||
name: '📅 Week Warrior',
|
||||
description: 'Practice 7 days in a row',
|
||||
condition: (stats) => stats.dailyStreak >= 7,
|
||||
points: 150,
|
||||
rarity: 'epic'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Achievement Checker
|
||||
```javascript
|
||||
function checkAchievements(stats, unlockedAchievements) {
|
||||
const newAchievements = [];
|
||||
|
||||
for (const [id, achievement] of Object.entries(ACHIEVEMENTS)) {
|
||||
if (!unlockedAchievements.includes(id)) {
|
||||
if (achievement.condition(stats)) {
|
||||
newAchievements.push(achievement);
|
||||
unlockedAchievements.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newAchievements;
|
||||
}
|
||||
```
|
||||
|
||||
### Achievement Display
|
||||
```javascript
|
||||
function showAchievementPopup(achievement) {
|
||||
const popup = document.createElement('div');
|
||||
popup.className = 'achievement-popup';
|
||||
popup.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: linear-gradient(135deg, ${getRarityColor(achievement.rarity)});
|
||||
color: white;
|
||||
padding: 20px 30px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
animation: slideIn 0.5s ease, pulse 0.5s ease 0.5s;
|
||||
z-index: 1000;
|
||||
font-weight: bold;
|
||||
min-width: 250px;
|
||||
`;
|
||||
|
||||
popup.innerHTML = `
|
||||
<div style="font-size: 2em; text-align: center;">${achievement.name}</div>
|
||||
<div style="margin-top: 10px; text-align: center;">${achievement.description}</div>
|
||||
<div style="margin-top: 10px; text-align: center; font-size: 1.5em;">
|
||||
+${achievement.points} points
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(popup);
|
||||
|
||||
// Play sound
|
||||
playAchievementSound(achievement.rarity);
|
||||
|
||||
// Remove after 4 seconds
|
||||
setTimeout(() => {
|
||||
popup.style.animation = 'slideOut 0.5s ease';
|
||||
setTimeout(() => popup.remove(), 500);
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
function getRarityColor(rarity) {
|
||||
const colors = {
|
||||
common: '#909090, #606060',
|
||||
uncommon: '#4CAF50, #388E3C',
|
||||
rare: '#2196F3, #1565C0',
|
||||
epic: '#9C27B0, #6A1B9A',
|
||||
legendary: '#FFC107, #F57C00'
|
||||
};
|
||||
return colors[rarity] || colors.common;
|
||||
}
|
||||
```
|
||||
|
||||
## Progress Tracking
|
||||
|
||||
### Stats Structure
|
||||
```javascript
|
||||
const playerStats = {
|
||||
// Basic counts
|
||||
problemsAttempted: 0,
|
||||
problemsCompleted: 0,
|
||||
correctAnswers: 0,
|
||||
incorrectAnswers: 0,
|
||||
|
||||
// Streaks
|
||||
currentStreak: 0,
|
||||
longestStreak: 0,
|
||||
dailyStreak: 0,
|
||||
|
||||
// Performance
|
||||
averageTime: 0,
|
||||
fastestTime: Infinity,
|
||||
fastSolves: 0,
|
||||
perfectScores: 0,
|
||||
|
||||
// Exploration
|
||||
valuesExplored: 0,
|
||||
patternsFound: 0,
|
||||
|
||||
// Points & Level
|
||||
totalPoints: 0,
|
||||
level: 1,
|
||||
pointsToNextLevel: 100,
|
||||
|
||||
// Time tracking
|
||||
totalTimeSpent: 0,
|
||||
sessionsCount: 0,
|
||||
lastSessionDate: null,
|
||||
lateNightSessions: 0,
|
||||
earlyMorningSessions: 0,
|
||||
|
||||
// Achievements
|
||||
achievementsUnlocked: [],
|
||||
achievementPoints: 0
|
||||
};
|
||||
```
|
||||
|
||||
### Level System
|
||||
```javascript
|
||||
function calculateLevel(points) {
|
||||
// Level 1: 0-100 points
|
||||
// Level 2: 100-300 points
|
||||
// Level 3: 300-600 points
|
||||
// Formula: points_needed = 100 * level * (level + 1) / 2
|
||||
|
||||
let level = 1;
|
||||
let pointsNeeded = 100;
|
||||
let totalPointsForLevel = 0;
|
||||
|
||||
while (points >= totalPointsForLevel + pointsNeeded) {
|
||||
totalPointsForLevel += pointsNeeded;
|
||||
level++;
|
||||
pointsNeeded = 100 * level;
|
||||
}
|
||||
|
||||
return {
|
||||
level: level,
|
||||
currentPoints: points,
|
||||
pointsInLevel: points - totalPointsForLevel,
|
||||
pointsToNextLevel: pointsNeeded,
|
||||
progress: (points - totalPointsForLevel) / pointsNeeded
|
||||
};
|
||||
}
|
||||
|
||||
function checkLevelUp(oldPoints, newPoints) {
|
||||
const oldLevel = calculateLevel(oldPoints).level;
|
||||
const newLevel = calculateLevel(newPoints).level;
|
||||
|
||||
if (newLevel > oldLevel) {
|
||||
showLevelUpAnimation(newLevel);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
### Progress Visualization
|
||||
```javascript
|
||||
function drawProgressBar(progress, canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = canvas.width;
|
||||
const height = 40;
|
||||
|
||||
// Background
|
||||
ctx.fillStyle = '#E0E0E0';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
// Progress fill
|
||||
const gradient = ctx.createLinearGradient(0, 0, width * progress, 0);
|
||||
gradient.addColorStop(0, '#667eea');
|
||||
gradient.addColorStop(1, '#764ba2');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, width * progress, height);
|
||||
|
||||
// Border
|
||||
ctx.strokeStyle = '#333';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(0, 0, width, height);
|
||||
|
||||
// Text
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = 'bold 18px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(`${Math.floor(progress * 100)}%`, width / 2, height / 2);
|
||||
}
|
||||
```
|
||||
|
||||
## Reward Animations
|
||||
|
||||
### Confetti
|
||||
```javascript
|
||||
function createConfetti() {
|
||||
const colors = ['#FF6347', '#FFD700', '#4CAF50', '#2196F3', '#9C27B0'];
|
||||
const confettiCount = 50;
|
||||
const confetti = [];
|
||||
|
||||
for (let i = 0; i < confettiCount; i++) {
|
||||
confetti.push({
|
||||
x: Math.random() * window.innerWidth,
|
||||
y: -20,
|
||||
vx: (Math.random() - 0.5) * 5,
|
||||
vy: Math.random() * 3 + 2,
|
||||
color: colors[Math.floor(Math.random() * colors.length)],
|
||||
size: Math.random() * 10 + 5,
|
||||
rotation: Math.random() * 360,
|
||||
rotationSpeed: (Math.random() - 0.5) * 10
|
||||
});
|
||||
}
|
||||
|
||||
return confetti;
|
||||
}
|
||||
|
||||
function animateConfetti(confetti, ctx) {
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
|
||||
let active = 0;
|
||||
confetti.forEach(piece => {
|
||||
piece.x += piece.vx;
|
||||
piece.y += piece.vy;
|
||||
piece.vy += 0.1; // Gravity
|
||||
piece.rotation += piece.rotationSpeed;
|
||||
|
||||
if (piece.y < ctx.canvas.height) {
|
||||
active++;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(piece.x, piece.y);
|
||||
ctx.rotate(piece.rotation * Math.PI / 180);
|
||||
ctx.fillStyle = piece.color;
|
||||
ctx.fillRect(-piece.size / 2, -piece.size / 2, piece.size, piece.size);
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
|
||||
if (active > 0) {
|
||||
requestAnimationFrame(() => animateConfetti(confetti, ctx));
|
||||
}
|
||||
}
|
||||
|
||||
function celebrateWithConfetti() {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
canvas.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
`;
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const confetti = createConfetti();
|
||||
|
||||
animateConfetti(confetti, ctx);
|
||||
|
||||
setTimeout(() => canvas.remove(), 5000);
|
||||
}
|
||||
```
|
||||
|
||||
### Score Popup
|
||||
```javascript
|
||||
function showScorePopup(points, x, y) {
|
||||
const popup = document.createElement('div');
|
||||
popup.textContent = `+${points}`;
|
||||
popup.style.cssText = `
|
||||
position: absolute;
|
||||
left: ${x}px;
|
||||
top: ${y}px;
|
||||
color: #FFD700;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
animation: floatUp 1s ease-out forwards;
|
||||
pointer-events: none;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
||||
z-index: 1000;
|
||||
`;
|
||||
|
||||
document.body.appendChild(popup);
|
||||
setTimeout(() => popup.remove(), 1000);
|
||||
}
|
||||
|
||||
// Add CSS animation
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes floatUp {
|
||||
from {
|
||||
transform: translateY(0) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translateY(-100px) scale(1.5);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
```
|
||||
|
||||
## Challenge Modes
|
||||
|
||||
### Time Attack
|
||||
```javascript
|
||||
class TimeAttackMode {
|
||||
constructor(duration = 60) {
|
||||
this.duration = duration;
|
||||
this.timeRemaining = duration;
|
||||
this.problemsSolved = 0;
|
||||
this.totalPoints = 0;
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
start() {
|
||||
this.isActive = true;
|
||||
this.timer = setInterval(() => {
|
||||
this.timeRemaining--;
|
||||
this.updateDisplay();
|
||||
|
||||
if (this.timeRemaining <= 0) {
|
||||
this.end();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
solve(points) {
|
||||
if (this.isActive) {
|
||||
this.problemsSolved++;
|
||||
this.totalPoints += points;
|
||||
this.generateNewProblem();
|
||||
}
|
||||
}
|
||||
|
||||
end() {
|
||||
this.isActive = false;
|
||||
clearInterval(this.timer);
|
||||
this.showResults();
|
||||
}
|
||||
|
||||
showResults() {
|
||||
const results = {
|
||||
problemsSolved: this.problemsSolved,
|
||||
totalPoints: this.totalPoints,
|
||||
averageTime: this.duration / this.problemsSolved,
|
||||
rating: this.calculateRating()
|
||||
};
|
||||
|
||||
displayResults(results);
|
||||
}
|
||||
|
||||
calculateRating() {
|
||||
if (this.problemsSolved >= 30) return '🏆 Master';
|
||||
if (this.problemsSolved >= 20) return '⭐ Expert';
|
||||
if (this.problemsSolved >= 15) return '👍 Good';
|
||||
if (this.problemsSolved >= 10) return '✓ Average';
|
||||
return '💪 Keep Practicing';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Accuracy Challenge
|
||||
```javascript
|
||||
class AccuracyChallenge {
|
||||
constructor(problemCount = 10) {
|
||||
this.problemCount = problemCount;
|
||||
this.currentProblem = 0;
|
||||
this.correctAnswers = 0;
|
||||
this.mistakes = 0;
|
||||
this.maxMistakes = 3;
|
||||
}
|
||||
|
||||
checkAnswer(userAnswer, correctAnswer) {
|
||||
if (userAnswer === correctAnswer) {
|
||||
this.correctAnswers++;
|
||||
this.currentProblem++;
|
||||
celebrateCorrect();
|
||||
|
||||
if (this.currentProblem >= this.problemCount) {
|
||||
this.complete();
|
||||
} else {
|
||||
this.nextProblem();
|
||||
}
|
||||
} else {
|
||||
this.mistakes++;
|
||||
|
||||
if (this.mistakes >= this.maxMistakes) {
|
||||
this.fail();
|
||||
} else {
|
||||
showMistakeWarning(this.maxMistakes - this.mistakes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
complete() {
|
||||
const accuracy = (this.correctAnswers / this.problemCount) * 100;
|
||||
showCompletionScreen({
|
||||
success: true,
|
||||
accuracy: accuracy,
|
||||
perfect: accuracy === 100,
|
||||
reward: this.calculateReward(accuracy)
|
||||
});
|
||||
}
|
||||
|
||||
fail() {
|
||||
showCompletionScreen({
|
||||
success: false,
|
||||
correctAnswers: this.correctAnswers,
|
||||
totalProblems: this.problemCount
|
||||
});
|
||||
}
|
||||
|
||||
calculateReward(accuracy) {
|
||||
if (accuracy === 100) return 200;
|
||||
if (accuracy >= 90) return 150;
|
||||
if (accuracy >= 80) return 100;
|
||||
if (accuracy >= 70) return 75;
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Sound Effects
|
||||
|
||||
### Audio System
|
||||
```javascript
|
||||
const sounds = {
|
||||
correct: 'data:audio/wav;base64,...', // Positive chime
|
||||
incorrect: 'data:audio/wav;base64,...', // Gentle buzz
|
||||
achievement: 'data:audio/wav;base64,...', // Fanfare
|
||||
levelUp: 'data:audio/wav;base64,...', // Ascending notes
|
||||
click: 'data:audio/wav;base64,...' // Click sound
|
||||
};
|
||||
|
||||
function playSound(soundName) {
|
||||
if (!sounds[soundName]) return;
|
||||
|
||||
const audio = new Audio(sounds[soundName]);
|
||||
audio.volume = 0.3;
|
||||
audio.play().catch(() => {
|
||||
// Ignore if user hasn't interacted yet
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
These gamification patterns provide:
|
||||
- Comprehensive points and achievement systems
|
||||
- Progressive difficulty with level systems
|
||||
- Engaging reward animations
|
||||
- Multiple challenge modes
|
||||
- Progress tracking and visualization
|
||||
- Sound effects and celebrations
|
||||
|
||||
Use these to make math learning addictively fun!
|
||||
Reference in New Issue
Block a user