diff --git a/web-app/js/projects.js b/web-app/js/projects.js index 1e054f7..bf47439 100644 --- a/web-app/js/projects.js +++ b/web-app/js/projects.js @@ -1,4 +1,4 @@ -// Project Registry +// Project Registry // Each project's HTML and logic lives in its own file under js/projects/ @@ -339,229 +339,7 @@ function initDiceRolling() { // ============================================ // COIN FLIP // ============================================ -function getCoinFlipHTML() { - return ` -
-

🪙 Coin Flip

-
-
-
-
👑
-
-
🦅
-
-
-
- -
Click to Flip!
- - - -
-
- Heads: - 0 -
-
- Tails: - 0 -
-
-
-
- - - `; -} - -function initCoinFlip() { - const coin = document.getElementById('coin'); - const coinScene = coin.closest('.coin-scene'); - const flipBtn = document.getElementById('flipCoin'); - const result = document.getElementById('coinResult'); - const headsCountEl = document.getElementById('headsCount'); - const tailsCountEl = document.getElementById('tailsCount'); - let headsCount = 0; - let tailsCount = 0; - let spinCount = 0; - - function setCoinFace(isHeads, seed) { - const targetY = isHeads ? 0 : 180; - const flipX = 360 * (4 + (seed % 3)); - const flipY = 360 * (3 + (seed % 2)) + targetY; - coin.style.setProperty('--flip-x', `${flipX}deg`); - coin.style.setProperty('--flip-y', `${flipY}deg`); - } - - function triggerCoinLanding() { - coinScene.classList.remove('impact'); - void coinScene.offsetWidth; - coinScene.classList.add('impact'); - setTimeout(() => { - coinScene.classList.remove('impact'); - }, 460); - } - - flipBtn.addEventListener('click', () => { - flipBtn.disabled = true; - result.textContent = 'Flipping...'; - coinScene.classList.add('rolling'); - - const isHeads = Math.random() < 0.5; - spinCount += 1; - setCoinFace(isHeads, spinCount); - - setTimeout(() => { - coinScene.classList.remove('rolling'); - triggerCoinLanding(); - if (isHeads) { - result.textContent = '👑 Heads!'; - headsCount++; - headsCountEl.textContent = headsCount; - } else { - result.textContent = '🦅 Tails!'; - tailsCount++; - tailsCountEl.textContent = tailsCount; - } - flipBtn.disabled = false; - }, 1600); - }); -} +// (Coin Flip HTML & Logic loaded from modular file js/projects/coin-flip.js) // Continue with more projects in next message... // Additional Project Implementations diff --git a/web-app/js/projects/coin-flip.js b/web-app/js/projects/coin-flip.js index 17df499..72d3714 100644 --- a/web-app/js/projects/coin-flip.js +++ b/web-app/js/projects/coin-flip.js @@ -1,8 +1,30 @@ function getCoinFlipHTML() { return `
-

🪙 Coin Flip

+

🪙 Coin Flip Prediction Game

+ + +
+
+ Wins + 0 +
+
+ Losses + 0 +
+
+ Streak + 🔥 0 +
+
+ Best Streak + 🏆 0 +
+
+
+
👑
@@ -12,17 +34,36 @@ function getCoinFlipHTML() {
-
Click to Flip!
+ +
+

Select your prediction:

+
+
+ 👑 + Heads +
+
+ 🦅 + Tails +
+
+
+ + +
+
Select a prediction to start!
+
- + -
-
- Heads: + +
+
+ Total Heads: 0
-
- Tails: +
+ Total Tails: 0
@@ -32,23 +73,73 @@ function getCoinFlipHTML() { `; @@ -173,12 +409,73 @@ function initCoinFlip() { const coinScene = coin.closest('.coin-scene'); const flipBtn = document.getElementById('flipCoin'); const result = document.getElementById('coinResult'); + + // Core Elements const headsCountEl = document.getElementById('headsCount'); const tailsCountEl = document.getElementById('tailsCount'); - let headsCount = 0; - let tailsCount = 0; + const predictHeadsBtn = document.getElementById('predictHeads'); + const predictTailsBtn = document.getElementById('predictTails'); + const predictionWrapper = predictHeadsBtn.closest('.prediction-cards'); + + // Scoreboard Elements + const statsWinsEl = document.getElementById('statsWins'); + const statsLossesEl = document.getElementById('statsLosses'); + const statsStreakEl = document.getElementById('statsStreak'); + const statsBestStreakEl = document.getElementById('statsBestStreak'); + + // Retrieve stats from localStorage + let wins = parseInt(localStorage.getItem('coinflip_wins') || '0'); + let losses = parseInt(localStorage.getItem('coinflip_losses') || '0'); + let streak = parseInt(localStorage.getItem('coinflip_streak') || '0'); + let bestStreak = parseInt(localStorage.getItem('coinflip_best_streak') || '0'); + let headsCount = parseInt(localStorage.getItem('coinflip_heads') || '0'); + let tailsCount = parseInt(localStorage.getItem('coinflip_tails') || '0'); let spinCount = 0; + let selectedPrediction = null; + + // Render Initial Stats + function updateStatsUI() { + statsWinsEl.textContent = wins; + statsLossesEl.textContent = losses; + statsStreakEl.textContent = `🔥 ${streak}`; + statsBestStreakEl.textContent = `🏆 ${bestStreak}`; + headsCountEl.textContent = headsCount; + tailsCountEl.textContent = tailsCount; + } + updateStatsUI(); + + // Prediction cards event listeners + function selectPrediction(prediction) { + selectedPrediction = prediction; + + predictHeadsBtn.classList.remove('selected'); + predictTailsBtn.classList.remove('selected'); + + if (prediction === 'heads') { + predictHeadsBtn.classList.add('selected'); + result.textContent = 'Predicted Heads! Ready to flip.'; + } else if (prediction === 'tails') { + predictTailsBtn.classList.add('selected'); + result.textContent = 'Predicted Tails! Ready to flip.'; + } + + result.className = 'coin-result'; // reset any win/lose classes + flipBtn.disabled = false; + } + + predictHeadsBtn.addEventListener('click', () => { + if (!flipBtn.disabled || selectedPrediction === null) { + selectPrediction('heads'); + } + }); + predictTailsBtn.addEventListener('click', () => { + if (!flipBtn.disabled || selectedPrediction === null) { + selectPrediction('tails'); + } + }); + + // 3D Coin Rotate Calculations function setCoinFace(isHeads, seed) { const targetY = isHeads ? 0 : 180; const flipX = 360 * (4 + (seed % 3)); @@ -187,6 +484,7 @@ function initCoinFlip() { coin.style.setProperty('--flip-y', `${flipY}deg`); } + // Landing bounce effect function triggerCoinLanding() { coinScene.classList.remove('impact'); void coinScene.offsetWidth; @@ -196,28 +494,66 @@ function initCoinFlip() { }, 460); } + // Main flip trigger flipBtn.addEventListener('click', () => { + if (!selectedPrediction) return; + + // Freeze controls during flip flipBtn.disabled = true; + predictionWrapper.classList.add('disabled-cards'); result.textContent = 'Flipping...'; + result.className = 'coin-result flipping'; coinScene.classList.add('rolling'); const isHeads = Math.random() < 0.5; + const flipResult = isHeads ? 'heads' : 'tails'; spinCount += 1; + setCoinFace(isHeads, spinCount); setTimeout(() => { coinScene.classList.remove('rolling'); triggerCoinLanding(); + + // Check win/loss logic + const isCorrect = (flipResult === selectedPrediction); + + // Lifetime stats update if (isHeads) { - result.textContent = '👑 Heads!'; headsCount++; - headsCountEl.textContent = headsCount; } else { - result.textContent = '🦅 Tails!'; tailsCount++; - tailsCountEl.textContent = tailsCount; } + + if (isCorrect) { + wins++; + streak++; + if (streak > bestStreak) { + bestStreak = streak; + } + result.textContent = isHeads ? '👑 Heads! Correct prediction! 🎉' : '🦅 Tails! Correct prediction! 🎉'; + result.className = 'coin-result win'; + } else { + losses++; + streak = 0; + result.textContent = isHeads ? '👑 Heads! Wrong prediction. 😢' : '🦅 Tails! Wrong prediction. 😢'; + result.className = 'coin-result lose'; + } + + // Persist scores + localStorage.setItem('coinflip_wins', wins); + localStorage.setItem('coinflip_losses', losses); + localStorage.setItem('coinflip_streak', streak); + localStorage.setItem('coinflip_best_streak', bestStreak); + localStorage.setItem('coinflip_heads', headsCount); + localStorage.setItem('coinflip_tails', tailsCount); + + // Update scoreboard & stats views + updateStatsUI(); + + // Re-enable controls flipBtn.disabled = false; + predictionWrapper.classList.remove('disabled-cards'); }, 1600); }); }