Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions web-app/js/hero-canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
Fixed: canvas now fills 100% of hero section
═══════════════════════════════════════════ */
const canvas = document.getElementById('boardCanvas');
const ctx = canvas.getContext('2d');

/* Force canvas to fill the hero section absolutely */
if (canvas) {
const ctx = canvas.getContext('2d');

/* Force canvas to fill the hero section absolutely */
(function positionCanvas() {
const hero = canvas.closest('.hero-section') || canvas.parentElement;
if (hero && getComputedStyle(hero).position === 'static') {
Expand Down Expand Up @@ -289,6 +291,7 @@ function loop() {
tokens.forEach(t => { t.update(); t.draw(); });
drawDice();
requestAnimationFrame(loop);
}
}

loop();
loop();
}
26 changes: 0 additions & 26 deletions web-app/js/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -3471,31 +3471,6 @@ function getTowerOfHanoiHTML() {
`;
}

function initTowerOfHanoi() {
const canvas = document.getElementById('hanoiCanvas');
const ctx = canvas.getContext('2d');
const diskCountInput = document.getElementById('diskCount');
const solveBtn = document.getElementById('solveBtn');
const resetBtn = document.getElementById('resetHanoi');
const moveCountEl = document.getElementById('moveCount');
const optimalMovesEl = document.getElementById('optimalMoves');

let towers = [[], [], []];
let diskCount = 3;
let moveCount = 0;
let isAnimating = false;
let shouldStop = false;

const towerX = [200, 400, 600];
const baseY = 350;
const diskHeight = 20;
const maxDiskWidth = 120;
const colors = ['#ff6b6b', '#f59e0b', '#10b981', '#06b6d4', '#6366f1', '#8b5cf6', '#ec4899'];

function initTowers() {
towers = [[], [], []];
moveCount = 0;
diskCount = parseInt(diskCountInput.value) || 3;

//Reset animation state
isAnimating = false;
Expand Down Expand Up @@ -3841,7 +3816,6 @@ function initializeProject(projectName) {
initializers[projectName]();
}
}
}

//Removed Redundant game and project Logics and seperated them to different individual files located at (web-app/js/projects/)

Expand Down
78 changes: 68 additions & 10 deletions web-app/js/projects/whack-a-mole.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,39 +45,89 @@ function initWhackaMole() {
let timeLeft = 30;
let gameActive = false;
let activeIndex = -1;
let lastActiveIndex = -1;
let lastActiveTime = 0;
let timerId = null;
let moleId = null;

const holes = Array.from({ length: 9 }, (_, index) => {
const button = document.createElement('button');
button.type = 'button';
button.className = 'whack-hole';
button.textContent = '🕳️';
button.setAttribute('aria-label', `Hole ${index + 1}`);
button.addEventListener('click', () => {
if (!gameActive || index !== activeIndex) return;

const handleHit = (e) => {
if (e) e.preventDefault();
if (!gameActive) return;

const isDirectHit = (index === activeIndex);
const isGraceHit = (index === lastActiveIndex && (Date.now() - lastActiveTime) < 150);

if (!isDirectHit && !isGraceHit) return;

score += 1;
scoreEl.textContent = String(score);
messageEl.textContent = 'Hit!';
messageEl.textContent = 'Hit! 🔨';
button.textContent = '💥';
button.classList.remove('active');

// Set to -1 immediately to prevent double hits
activeIndex = -1;
lastActiveIndex = -1;

clearTimeout(moleId);
showMole();
});
moleId = setTimeout(showMole, 250);
};

button.addEventListener('pointerdown', handleHit);
button.addEventListener('click', handleHit);

board.appendChild(button);
return button;
});

function showMole() {
holes.forEach(hole => hole.classList.remove('active'));
activeIndex = Math.floor(Math.random() * holes.length);
if (!gameActive) return;

// Save previous active mole state for grace period
if (activeIndex !== -1) {
lastActiveIndex = activeIndex;
lastActiveTime = Date.now();
}

holes.forEach(hole => {
hole.classList.remove('active');
hole.textContent = '🕳️';
});

// Choose a new hole, ensuring it is different from the current one
let newIndex;
do {
newIndex = Math.floor(Math.random() * holes.length);
} while (newIndex === activeIndex && holes.length > 1);

activeIndex = newIndex;
holes[activeIndex].classList.add('active');
holes[activeIndex].textContent = '🐭';

moleId = setTimeout(showMole, 850);
}

function stopGame(finalMessage) {
gameActive = false;
clearInterval(timerId);
timerId = null;
clearTimeout(moleId);
holes.forEach(hole => hole.classList.remove('active'));
moleId = null;
activeIndex = -1;
lastActiveIndex = -1;
holes.forEach(hole => {
hole.classList.remove('active');
hole.textContent = '🕳️';
});
messageEl.textContent = finalMessage;
startBtn.disabled = false;
}

function startGame() {
Expand All @@ -87,29 +137,37 @@ function initWhackaMole() {
scoreEl.textContent = '0';
timeEl.textContent = '30';
messageEl.textContent = 'Go!';
startBtn.disabled = true;
clearInterval(timerId);
clearTimeout(moleId);
showMole();
timerId = setInterval(() => {
timeLeft -= 1;
timeEl.textContent = String(timeLeft);
if (timeLeft <= 0) {
stopGame(`Time! Final score: ${score}`);
}
}, 1000);
showMole();
}

startBtn.addEventListener('click', startGame);
resetBtn.addEventListener('click', () => {
clearInterval(timerId);
timerId = null;
clearTimeout(moleId);
moleId = null;
score = 0;
timeLeft = 30;
gameActive = false;
activeIndex = -1;
holes.forEach(hole => hole.classList.remove('active'));
lastActiveIndex = -1;
holes.forEach(hole => {
hole.classList.remove('active');
hole.textContent = '🕳️';
});
scoreEl.textContent = '0';
timeEl.textContent = '30';
messageEl.textContent = 'Hit the mole when it appears.';
startBtn.disabled = false;
});
}
Loading