Files
gh-jamesrochabrun-skills-al…/skills/math-teacher/references/gamification.md
2025-11-29 18:48:58 +08:00

14 KiB
Raw Blame History

Gamification Reference

Game mechanics, reward systems, and engagement techniques for math learning.

Points System

Base Points

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

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

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

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

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

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

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

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

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

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

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

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

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

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!