636 lines
14 KiB
Markdown
636 lines
14 KiB
Markdown
# 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!
|